var FormKitVue = (function (exports, vue) {
    'use strict';

    /**
     * Commonly shared utility functions between official FormKit packages.
     *
     * You can add this package by using `npm install @formkit/utils` or `yarn add @formkit/utils`.
     *
     * @packageDocumentation
     */
    /**
     * Explicit keys that should always be cloned.
     */
    const explicitKeys = [
        '__key',
        '__init',
        '__shim',
        '__original',
        '__index',
        '__prevKey',
    ];
    /**
     * Generates a random string.
     *
     * @example
     *
     * ```javascript
     * import { token } from '@formkit/utils'
     *
     * const tk = token()
     * // 'jkbyqnphqm'
     * ```
     *
     * @returns string
     *
     * @public
     */
    function token() {
        return Math.random().toString(36).substring(2, 15);
    }
    /**
     * Given 2 arrays, return them as a combined array with no duplicates.
     *
     * @param arr1 - First array.
     * @param arr2 - Second array.
     *
     * @returns `any[]`
     *
     * @public
     */
    function dedupe(arr1, arr2) {
        const original = arr1 instanceof Set ? arr1 : new Set(arr1);
        if (arr2)
            arr2.forEach((item) => original.add(item));
        return [...original];
    }
    /**
     * Checks if the given property exists on the given object.
     *
     * @param obj - An object to check.
     * @param property - The property to check.
     *
     * @returns `boolean`
     *
     * @public
     */
    function has(obj, property) {
        return Object.prototype.hasOwnProperty.call(obj, property);
    }
    /**
     * Compare two values for equality, optionally at depth.
     *
     * @param valA - First value.
     * @param valB - Second value.
     * @param deep - If it will compare deeply if it's an object.
     * @param explicit - An array of keys to explicity check.
     *
     * @returns `boolean`
     *
     * @public
     */
    function eq(valA, // eslint-disable-line
    valB, // eslint-disable-line
    deep = true, explicit = ['__key']) {
        if (valA === valB)
            return true;
        if (typeof valB === 'object' && typeof valA === 'object') {
            if (valA instanceof Map)
                return false;
            if (valA instanceof Set)
                return false;
            if (valA instanceof Date)
                return false;
            if (valA === null || valB === null)
                return false;
            if (Object.keys(valA).length !== Object.keys(valB).length)
                return false;
            for (const k of explicit) {
                if ((k in valA || k in valB) && valA[k] !== valB[k])
                    return false;
            }
            for (const key in valA) {
                if (!(key in valB))
                    return false;
                if (valA[key] !== valB[key] && !deep)
                    return false;
                if (deep && !eq(valA[key], valB[key], deep, explicit))
                    return false;
            }
            return true;
        }
        return false;
    }
    /**
     * Determines if a value is empty or not.
     *
     * @param value - The value to check if it's empty.
     *
     * @returns `boolean`
     *
     * @public
     */
    function empty(value // eslint-disable-line
    ) {
        const type = typeof value;
        if (type === 'number')
            return false;
        if (value === undefined)
            return true;
        if (type === 'string') {
            return value === '';
        }
        if (type === 'object') {
            if (value === null)
                return true;
            for (const _i in value)
                return false;
            if (value instanceof RegExp)
                return false;
            if (value instanceof Date)
                return false;
            return true;
        }
        return false;
    }
    /**
     * Escape a string for use in regular expressions.
     *
     * @param string - String to be escaped.
     *
     * @returns `string`
     *
     * @public
     */
    function escapeExp(string) {
        // $& means the whole matched string
        return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    }
    /**
     * Given a string date format, return a regex to match against.
     *
     * @param format - String to be transformed to RegExp.
     *
     * @example
     *
     * ```javascript
     * regexForFormat('MM') // returns '(0[1-9]|1[012])'
     * ```
     *
     * @returns `RegExp`
     *
     * @public
     */
    function regexForFormat(format) {
        const escaped = `^${escapeExp(format)}$`;
        const formats = {
            MM: '(0[1-9]|1[012])',
            M: '([1-9]|1[012])',
            DD: '([012][0-9]|3[01])',
            D: '([012]?[0-9]|3[01])',
            YYYY: '\\d{4}',
            YY: '\\d{2}',
        };
        const tokens = Object.keys(formats);
        return new RegExp(tokens.reduce((regex, format) => {
            return regex.replace(format, formats[format]);
        }, escaped));
    }
    /**
     * Determines if an object is an object.
     *
     * @param o - The value to be checked.
     *
     * @returns `boolean`
     *
     * @public
     */
    // eslint-disable-next-line @typescript-eslint/ban-types
    function isRecord(o) {
        return Object.prototype.toString.call(o) === '[object Object]';
    }
    /**
     * Checks if an object is a simple array or record.
     *
     * @param o - Value to be checked.
     *
     * @returns `boolean`
     *
     * @public
     */
    function isObject(o) {
        return isRecord(o) || Array.isArray(o);
    }
    /**
     * Attempts to determine if an object is a POJO (Plain Old JavaScript Object).
     * Mostly lifted from is-plain-object: https://github.com/jonschlinkert/is-plain-object
     * Copyright (c) 2014-2017, Jon Schlinkert.
     *
     * @param o - The value to be checked.
     *
     * @returns `boolean`
     *
     * @public
     */
    // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
    function isPojo(o) {
        if (isRecord(o) === false)
            return false;
        if (o.__FKNode__ || o.__POJO__ === false)
            return false;
        const ctor = o.constructor;
        if (ctor === undefined)
            return true;
        const prot = ctor.prototype;
        if (isRecord(prot) === false)
            return false;
        if (prot.hasOwnProperty('isPrototypeOf') === false) {
            return false;
        }
        return true;
    }
    /**
     * Recursively merge data from additional into original returning a new object.
     *
     * @param original - The original array.
     * @param additional - The array to merge.
     * @param extendArrays - If it will extend/concatenate array values instead of
     * replacing them.
     * @param ignoreUndefined - If it will preserve values from the original object
     * even if the additional object has those values set to undefined.
     *
     * @returns `Record<string, any> | string | null`
     *
     * @public
     */
    function extend(original, additional, extendArrays = false, ignoreUndefined = false) {
        if (additional === null)
            return null;
        const merged = {};
        if (typeof additional === 'string')
            return additional;
        for (const key in original) {
            if (has(additional, key) &&
                (additional[key] !== undefined || !ignoreUndefined)) {
                if (extendArrays &&
                    Array.isArray(original[key]) &&
                    Array.isArray(additional[key])) {
                    merged[key] = original[key].concat(additional[key]);
                    continue;
                }
                if (additional[key] === undefined) {
                    continue;
                }
                if (isPojo(original[key]) && isPojo(additional[key])) {
                    merged[key] = extend(original[key], additional[key], extendArrays, ignoreUndefined);
                }
                else {
                    merged[key] = additional[key];
                }
            }
            else {
                merged[key] = original[key];
            }
        }
        for (const key in additional) {
            if (!has(merged, key) && additional[key] !== undefined) {
                merged[key] = additional[key];
            }
        }
        return merged;
    }
    /**
     * Determine if the given string is fully quoted.
     *
     * @example
     *
     * ```javascript
     * hello - false
     * "hello" - true
     * 'world' - true
     * "hello"=="world" - false
     * "hello'this'" - false
     * "hello"'there' - false
     * "hello""there" - false
     * 'hello === world' - true
     * ```
     *
     * @param str - The string to check.
     *
     * @returns `boolean`
     *
     * @public
     */
    function isQuotedString(str) {
        // quickly return false if the value is note quoted
        if (str[0] !== '"' && str[0] !== "'")
            return false;
        if (str[0] !== str[str.length - 1])
            return false;
        const quoteType = str[0];
        for (let p = 1; p < str.length; p++) {
            if (str[p] === quoteType &&
                (p === 1 || str[p - 1] !== '\\') &&
                p !== str.length - 1) {
                return false;
            }
        }
        return true;
    }
    /**
     * Remove extra escape characters.
     *
     * @param str - String to remove extra escape characters from.
     *
     * @returns `string`
     *
     * @public
     */
    function rmEscapes(str) {
        if (!str.length)
            return '';
        let clean = '';
        let lastChar = '';
        for (let p = 0; p < str.length; p++) {
            const char = str.charAt(p);
            if (char !== '\\' || lastChar === '\\') {
                clean += char;
            }
            lastChar = char;
        }
        return clean;
    }
    /**
     * Filters out values from an object that should not be considered "props" of
     * a core node, like "value" and "name".
     *
     * @param sets - The arrays to get values filtered out of.
     *
     * @returns `Record<string, any>`
     *
     * @public
     */
    function nodeProps(...sets) {
        return sets.reduce((valid, props) => {
            const { value, name, modelValue, config, plugins, ...validProps } = props; // eslint-disable-line
            return Object.assign(valid, validProps);
        }, {});
    }
    /**
     * Parse a string for comma-separated arguments.
     *
     * @param str - String to parse arguments from.
     *
     * @returns `string[]`
     *
     * @public
     */
    function parseArgs(str) {
        const args = [];
        let arg = '';
        let depth = 0;
        let quote = '';
        let lastChar = '';
        for (let p = 0; p < str.length; p++) {
            const char = str.charAt(p);
            if (char === quote && lastChar !== '\\') {
                quote = '';
            }
            else if ((char === "'" || char === '"') && !quote && lastChar !== '\\') {
                quote = char;
            }
            else if (char === '(' && !quote) {
                depth++;
            }
            else if (char === ')' && !quote) {
                depth--;
            }
            if (char === ',' && !quote && depth === 0) {
                args.push(arg);
                arg = '';
            }
            else if (char !== ' ' || quote) {
                arg += char;
            }
            lastChar = char;
        }
        if (arg) {
            args.push(arg);
        }
        return args;
    }
    /**
     * Return a new (shallow) object with any desired props removed.
     *
     * @param obj - The starting object.
     * @param toRemove - The array of properties to remove. Accepts strings or
     * regular expressions.
     *
     * @returns `Record<string, any>`
     *
     * @public
     */
    function except(obj, toRemove) {
        const clean = {};
        const exps = toRemove.filter((n) => n instanceof RegExp);
        const keysToRemove = new Set(toRemove);
        for (const key in obj) {
            if (!keysToRemove.has(key) && !exps.some((exp) => exp.test(key))) {
                clean[key] = obj[key];
            }
        }
        return clean;
    }
    /**
     * Extracts a set of keys from a given object. Importantly, this will extract
     * values even if they are not set on the original object — they will just have
     * an undefined value.
     *
     * @param obj - The object to get values from.
     * @param include - The array of items to get.
     *
     * @returns `Record<string, any>`
     *
     * @public
     */
    function only(obj, include) {
        const clean = {};
        const exps = include.filter((n) => n instanceof RegExp);
        include.forEach((key) => {
            if (!(key instanceof RegExp)) {
                clean[key] = obj[key];
            }
        });
        Object.keys(obj).forEach((key) => {
            if (exps.some((exp) => exp.test(key))) {
                clean[key] = obj[key];
            }
        });
        return clean;
    }
    /**
     * This converts kebab-case to camelCase. It ONLY converts from kebab to camel.
     *
     * @param str - String to be camel cased.
     *
     * @returns `string`
     *
     * @public
     */
    function camel(str) {
        return str.replace(/-([a-z0-9])/gi, (_s, g) => g.toUpperCase());
    }
    /**
     * This converts camel-case to kebab case. It ONLY converts from camel to kebab.
     *
     * @param str - String to be kebabed.
     *
     * @returns `string`
     *
     * @public
     */
    function kebab(str) {
        return str
            .replace(/([a-z0-9])([A-Z])/g, (_s, trail, cap) => trail + '-' + cap.toLowerCase())
            .replace(' ', '-')
            .toLowerCase();
    }
    /**
     * Shallowly clones the given object.
     *
     * @param obj - Object to be shallowly cloned.
     * @param explicit - The array of keys to be explicity cloned.
     *
     * @returns `T`
     *
     * @public
     */
    function shallowClone(obj, explicit = explicitKeys) {
        if (obj !== null && typeof obj === 'object') {
            let returnObject;
            if (Array.isArray(obj))
                returnObject = [...obj];
            else if (isPojo(obj))
                returnObject = { ...obj };
            if (returnObject) {
                applyExplicit(obj, returnObject, explicit);
                return returnObject;
            }
        }
        return obj;
    }
    /**
     * Perform a recursive clone on a given object. Only intended to be used
     * for simple objects like arrays and POJOs.
     *
     * @param obj - Object to be cloned.
     * @param explicit - Array of items to be explicity cloned.
     *
     * @returns `T`
     *
     * @public
     */
    function clone(obj, explicit = explicitKeys) {
        if (obj === null ||
            obj instanceof RegExp ||
            obj instanceof Date ||
            obj instanceof Map ||
            obj instanceof Set ||
            (typeof File === 'function' && obj instanceof File))
            return obj;
        let returnObject;
        if (Array.isArray(obj)) {
            returnObject = obj.map((value) => {
                if (typeof value === 'object')
                    return clone(value, explicit);
                return value;
            });
        }
        else {
            returnObject = Object.keys(obj).reduce((newObj, key) => {
                newObj[key] =
                    typeof obj[key] === 'object'
                        ? clone(obj[key], explicit)
                        : obj[key];
                return newObj;
            }, {});
        }
        for (const key of explicit) {
            if (key in obj) {
                Object.defineProperty(returnObject, key, {
                    enumerable: false,
                    value: obj[key],
                });
            }
        }
        return returnObject;
    }
    /**
     * Clones anything. If the item is scalar, no worries, it passes it back. If it
     * is an object, it performs a (fast/loose) clone operation.
     *
     * @param obj - The value to be cloned.
     *
     * @returns `T`
     *
     * @public
     */
    function cloneAny(obj) {
        return typeof obj === 'object'
            ? clone(obj)
            : obj;
    }
    /**
     * Get a specific value via dot notation.
     *
     * @param obj - An object to fetch data from.
     * @param addr - An "address" in dot notation.
     *
     * @returns `unknown`
     *
     * @public
     */
    // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
    function getAt(obj, addr) {
        if (!obj || typeof obj !== 'object')
            return null;
        const segments = addr.split('.');
        let o = obj;
        for (const i in segments) {
            const segment = segments[i];
            if (has(o, segment)) {
                o = o[segment];
            }
            if (+i === segments.length - 1)
                return o;
            if (!o || typeof o !== 'object')
                return null;
        }
        return null;
    }
    /**
     * Determines if the value of a prop that is either present (true) or not
     * present (undefined). For example, the prop disabled should disable
     * by just existing, but what if it is set to the string "false" — then it
     * should not be disabled.
     *
     * @param value - Value to check for undefined.
     *
     * @returns `true | undefined`
     *
     * @public
     */
    function undefine(value) {
        return value !== undefined && value !== 'false' && value !== false
            ? true
            : undefined;
    }
    /**
     * Defines an object as an initial value.
     *
     * @param obj - Object to be added an initial value.
     *
     * @returns `T & { __init?: true }`
     *
     * @public
     */
    /* eslint-disable-next-line @typescript-eslint/ban-types */
    function init(obj) {
        return !Object.isFrozen(obj)
            ? Object.defineProperty(obj, '__init', {
                enumerable: false,
                value: true,
            })
            : obj;
    }
    /**
     * Turn any string into a URL/DOM-safe string.
     *
     * @param str - String to be slugified to a URL-safe string.
     *
     * @returns `string`
     *
     * @public
     */
    function slugify(str) {
        return str
            .normalize('NFD')
            .replace(/[\u0300-\u036f]/g, '')
            .toLowerCase()
            .replace(/[^a-z0-9]/g, ' ')
            .trim()
            .replace(/\s+/g, '-');
    }
    /**
     * Apply non enumerable properties to an object.
     *
     * @param original - Original object
     * @param obj - Objecto to aplly the values
     * @param explicit - Array of items to be explicity added.
     *
     * @returns `T`
     *
     * @public
     */
    // eslint-disable-next-line @typescript-eslint/ban-types
    function applyExplicit(original, obj, explicit) {
        for (const key of explicit) {
            if (key in original) {
                Object.defineProperty(obj, key, {
                    enumerable: false,
                    value: original[key],
                });
            }
        }
        return obj;
    }

    /**
     * Creates a new dispatcher that allows the addition/removal of middleware
     * functions, and the ability to dispatch a payload to all middleware.
     *
     * @returns FormKitDispatcher
     *
     * @internal
     */
    function createDispatcher$1() {
        const middleware = [];
        let currentIndex = 0;
        const use = (dispatchable) => middleware.push(dispatchable);
        const dispatch = (payload) => {
            const current = middleware[currentIndex];
            if (typeof current === 'function') {
                return current(payload, (explicitPayload) => {
                    currentIndex++;
                    return dispatch(explicitPayload === undefined ? payload : explicitPayload);
                });
            }
            currentIndex = 0;
            return payload;
        };
        use.dispatch = dispatch;
        use.unshift = (dispatchable) => middleware.unshift(dispatchable);
        use.remove = (dispatchable) => {
            const index = middleware.indexOf(dispatchable);
            if (index > -1)
                middleware.splice(index, 1);
        };
        return use;
    }

    /**
     * Creates a new event emitter, each node uses one of these to allow it to emit
     * events to local listeners and tree listeners.
     *
     * @returns FormKitEventEmitter
     *
     * @internal
     */
    function createEmitter() {
        const listeners = new Map();
        const receipts = new Map();
        let buffer = undefined;
        const emitter = (node, event) => {
            if (buffer) {
                buffer.set(event.name, [node, event]);
                return;
            }
            if (listeners.has(event.name)) {
                // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
                listeners.get(event.name).forEach((wrapper) => {
                    if (event.origin === node || wrapper.modifiers.includes('deep')) {
                        wrapper.listener(event);
                    }
                });
            }
            if (event.bubble) {
                node.bubble(event);
            }
        };
        /**
         * Add an event listener
         *
         * @param eventName - The name of the event to listen to
         * @param listener - The callback
         *
         * @returns string
         *
         * @internal
         */
        emitter.on = (eventName, listener) => {
            const [event, ...modifiers] = eventName.split('.');
            const receipt = listener.receipt || token();
            const wrapper = {
                modifiers,
                event,
                listener,
                receipt,
            };
            /* eslint-disable @typescript-eslint/no-non-null-assertion */
            listeners.has(event)
                ? listeners.get(event).push(wrapper)
                : listeners.set(event, [wrapper]);
            receipts.has(receipt)
                ? receipts.get(receipt).push(event)
                : receipts.set(receipt, [event]);
            /* eslint-enable @typescript-eslint/no-non-null-assertion */
            return receipt;
        };
        /**
         * Remove an event listener
         *
         * @param listenerOrReceipt - Either a receipt or the callback function.
         *
         * @internal
         */
        emitter.off = (receipt) => {
            var _a;
            if (receipts.has(receipt)) {
                (_a = receipts.get(receipt)) === null || _a === void 0 ? void 0 : _a.forEach((event) => {
                    const eventListeners = listeners.get(event);
                    if (Array.isArray(eventListeners)) {
                        listeners.set(event, eventListeners.filter((wrapper) => wrapper.receipt !== receipt));
                    }
                });
                receipts.delete(receipt);
            }
        };
        /**
         * Pause emitting values. Any events emitted while paused will not be emitted
         * but rather "stored" — and whichever events are emitted last will be output.
         * For example:
         * pause()
         * emit('foo', 1)
         * emit('foo', 2)
         * emit('bar', 3)
         * emit('bar', 4)
         * play()
         * // would result in
         * emit('foo', 2)
         * emit('bar', 4)
         * Optionally pauses all children as well.
         *
         * @param node - A node to pause all children on.
         *
         * @internal
         */
        emitter.pause = (node) => {
            if (!buffer)
                buffer = new Map();
            if (node) {
                node.walk((child) => child._e.pause());
            }
        };
        /**
         * Release the current event buffer.
         *
         * @param node - A node to unpause all children on.
         *
         * @internal
         */
        emitter.play = (node) => {
            if (!buffer)
                return;
            const events = buffer;
            buffer = undefined;
            events.forEach(([node, event]) => emitter(node, event));
            if (node) {
                node.walk((child) => child._e.play());
            }
        };
        return emitter;
    }
    /**
     * Emit an event from this node.
     *
     * @param node - The node that is emitting
     * @param context - The context of that node
     * @param name - The name of the event
     * @param payload - The payload to emit
     *
     * @returns FormKitNode
     *
     * @internal
     */
    function emit$1(node, context, name, payload, // eslint-disable-line @typescript-eslint/explicit-module-boundary-types,
    bubble = true) {
        context._e(node, {
            payload,
            name,
            bubble,
            origin: node,
        });
        return node;
    }
    /**
     * Send an event from the given node up it's ancestor tree.
     *
     * @param node -
     * @param _context -
     * @param event -
     *
     * @internal
     */
    function bubble(node, _context, event) {
        if (isNode(node.parent)) {
            node.parent._e(node.parent, event);
        }
        return node;
    }
    /**
     * Adds an event listener to the node for a specific event. The event name is a
     * simple string matching the name of the event to listen to. It can optionally
     * include modifiers like eventName.deep
     *
     * @param node -
     * @param context -
     * @param name -
     * @param listener -
     *
     * @returns FormKitNode
     *
     * @internal
     */
    function on(_node, context, name, listener) {
        return context._e.on(name, listener);
    }
    /**
     * Removes an event listener from a node by the returned receipt from .on().
     *
     * @param node - The node to remote the listener from
     * @param context - The context to remove
     * @param receipt - The receipt returned by .on()
     *
     * @returns FormKitNode
     *
     * @internal
     */
    function off(node, context, receipt) {
        context._e.off(receipt);
        return node;
    }

    /**
     * FormKit's global error handler.
     *
     * @public
     */
    const errorHandler = createDispatcher$1();
    errorHandler((error, next) => {
        if (!error.message)
            error.message = String(`E${error.code}`);
        return next(error);
    });
    /**
     * FormKit's global warning handler.
     *
     * @public
     */
    const warningHandler = createDispatcher$1();
    warningHandler((warning, next) => {
        if (!warning.message)
            warning.message = String(`W${warning.code}`);
        const result = next(warning);
        if (console && typeof console.warn === 'function')
            console.warn(result.message);
        return result;
    });
    /**
     * Globally emits a warning.
     *
     * @param code - The integer warning code.
     * @param data - Usually an object of information to include.
     *
     * @public
     */
    function warn(code, data = {}) {
        warningHandler.dispatch({ code, data });
    }
    /**
     * Emits an error. Generally should result in an exception.
     *
     * @param code - The integer error code.
     * @param data - Usually an object of information to include.
     *
     * @public
     */
    function error(code, data = {}) {
        throw Error(errorHandler.dispatch({ code, data }).message);
    }

    /**
     * Creates a new FormKitMessage object.
     *
     * @param conf - An object of optional properties of {@link FormKitMessage | FormKitMessage}.
     * @param node - A {@link @formkit/node#FormKitNode | FormKitNode}.
     *
     * @returns A {@link FormKitMessageProps | FormKitMessageProps}.
     *
     * @public
     */
    function createMessage(conf, node) {
        const m = {
            blocking: false,
            key: token(),
            meta: {},
            type: 'state',
            visible: true,
            ...conf,
        };
        if (node && m.value && m.meta.localize !== false) {
            m.value = node.t(m);
            m.meta.locale = node.config.locale;
        }
        return m;
    }
    /**
     * The available traps on the node's store.
     *
     * @internal
     */
    const storeTraps = {
        apply: applyMessages,
        set: setMessage,
        remove: removeMessage$1,
        filter: filterMessages,
        reduce: reduceMessages,
        release: releaseBuffer,
        touch: touchMessages,
    };
    /**
     * Creates a new FormKit message store.
     *
     * @internal
     */
    function createStore(_buffer = false) {
        const messages = {};
        let node;
        let buffer = _buffer;
        let _b = [];
        const _m = new Map();
        let _r = undefined;
        const store = new Proxy(messages, {
            get(...args) {
                const [_target, property] = args;
                if (property === 'buffer')
                    return buffer;
                if (property === '_b')
                    return _b;
                if (property === '_m')
                    return _m;
                if (property === '_r')
                    return _r;
                if (has(storeTraps, property)) {
                    return storeTraps[property].bind(null, messages, store, node);
                }
                return Reflect.get(...args);
            },
            set(_t, prop, value) {
                if (prop === '_n') {
                    node = value;
                    if (_r === '__n')
                        releaseMissed(node, store);
                    return true;
                }
                else if (prop === '_b') {
                    _b = value;
                    return true;
                }
                else if (prop === 'buffer') {
                    buffer = value;
                    return true;
                }
                else if (prop === '_r') {
                    _r = value;
                    return true;
                }
                error(101, node);
                return false;
            },
        });
        return store;
    }
    /**
     * Adds a new value to a FormKit message bag.
     *
     * @param messageStore - The message store itself
     * @param store - The store interface
     * @param node - The node this store belongs to
     * @param message - The message object
     *
     * @internal
     */
    function setMessage(messageStore, store, node, message) {
        if (store.buffer) {
            store._b.push([[message]]);
            return store;
        }
        if (messageStore[message.key] !== message) {
            if (typeof message.value === 'string' && message.meta.localize !== false) {
                // Expose the value to translation
                const previous = message.value;
                message.value = node.t(message);
                if (message.value !== previous) {
                    message.meta.locale = node.props.locale;
                }
            }
            const e = `message-${has(messageStore, message.key) ? 'updated' : 'added'}`;
            messageStore[message.key] = Object.freeze(node.hook.message.dispatch(message));
            node.emit(e, message);
        }
        return store;
    }
    /**
     * Run through each message in the store, and ensure it has been translated
     * to the proper language. This most frequently happens after a locale change.
     *
     * @internal
     */
    function touchMessages(messageStore, store) {
        for (const key in messageStore) {
            const message = { ...messageStore[key] };
            store.set(message);
        }
    }
    /**
     * Remove a message from the store.
     *
     * @param messageStore - The message store
     * @param store - The store interface
     * @param node - The node this store belongs to
     * @param key - The message key
     *
     * @returns FormKitStore
     *
     * @internal
     */
    function removeMessage$1(messageStore, store, node, key) {
        if (has(messageStore, key)) {
            const message = messageStore[key];
            delete messageStore[key];
            node.emit('message-removed', message);
        }
        if (store.buffer === true) {
            store._b = store._b.filter((buffered) => {
                buffered[0] = buffered[0].filter((m) => m.key !== key);
                return buffered[1] || buffered[0].length;
            });
        }
        return store;
    }
    /**
     * Iterates over all messages removing those that are no longer wanted.
     *
     * @param messageStore - The store itself
     * @param store - The store interface
     * @param node - The node to filter for
     * @param callback - A callback accepting a message and returning a boolean
     * @param type - Pre filtered by a given message type
     *
     * @internal
     */
    function filterMessages(messageStore, store, node, callback, type) {
        for (const key in messageStore) {
            const message = messageStore[key];
            if ((!type || message.type === type) && !callback(message)) {
                removeMessage$1(messageStore, store, node, key);
            }
        }
    }
    /**
     * Reduce the message store to some other generic value.
     *
     * @param messageStore - The store itself
     * @param _store - Unused but curried — the store interface itself
     * @param _node - The node owner of this store
     * @param reducer - The callback that performs the reduction
     * @param accumulator - The initial value
     *
     * @returns
     *
     * @internal
     */
    function reduceMessages(messageStore, _store, _node, reducer, accumulator) {
        for (const key in messageStore) {
            const message = messageStore[key];
            accumulator = reducer(accumulator, message);
        }
        return accumulator;
    }
    /**
     * @param messageStore - The store itself
     * @param _store - Unused but curried — the store interface itself
     * @param node - The node owner of this store
     * @param messages - An array of FormKitMessages to apply to this input, or an object of messages to apply to children.
     *
     * @internal
     */
    function applyMessages(_messageStore, store, node, messages, clear) {
        if (Array.isArray(messages)) {
            if (store.buffer) {
                store._b.push([messages, clear]);
                return;
            }
            // In this case we are applying messages to this node’s store.
            const applied = new Set(messages.map((message) => {
                store.set(message);
                return message.key;
            }));
            // Remove any messages that were not part of the initial apply:
            if (typeof clear === 'string') {
                store.filter((message) => message.type !== clear || applied.has(message.key));
            }
            else if (typeof clear === 'function') {
                store.filter((message) => !clear(message) || applied.has(message.key));
            }
        }
        else {
            for (const address in messages) {
                const child = node.at(address);
                if (child) {
                    child.store.apply(messages[address], clear);
                }
                else {
                    missed(node, store, address, messages[address], clear);
                }
            }
        }
    }
    /**
     * Creates an array of message arrays from strings.
     *
     * @param node - FormKitNode
     * @param errors - Arrays or objects of form errors or input errors
     *
     * @internal
     */
    function createMessages(node, ...errors) {
        const sourceKey = `${node.name}-set`;
        const make = (error) => createMessage({
            key: slugify(error),
            type: 'error',
            value: error,
            meta: { source: sourceKey, autoClear: true },
        });
        return errors
            .filter((m) => !!m)
            .map((errorSet) => {
            if (typeof errorSet === 'string')
                errorSet = [errorSet];
            if (Array.isArray(errorSet)) {
                return errorSet.map((error) => make(error));
            }
            else {
                const errors = {};
                for (const key in errorSet) {
                    if (Array.isArray(errorSet[key])) {
                        errors[key] = errorSet[key].map((error) => make(error));
                    }
                    else {
                        errors[key] = [make(errorSet[key])];
                    }
                }
                return errors;
            }
        });
    }
    /**
     * @param store - The store to apply this missed applications.
     * @param address - The address that was missed (a node path that didn't yet exist)
     * @param messages - The messages that should have been applied.
     * @param clear - The clearing function (if any)
     *
     * @internal
     */
    function missed(node, store, address, messages, clear) {
        var _a;
        const misses = store._m;
        if (!misses.has(address))
            misses.set(address, []);
        // The created receipt
        if (!store._r)
            store._r = releaseMissed(node, store);
        (_a = misses.get(address)) === null || _a === void 0 ? void 0 : _a.push([messages, clear]);
    }
    /**
     * Releases messages that were applied to a child via parent, but the child did
     * not exist. Once the child does exist, the created event for that child will
     * bubble to this point, and any stored applications will be applied serially.
     *
     * @param store - The store object.
     *
     * @internal
     */
    function releaseMissed(node, store) {
        return node.on('child.deep', ({ payload: child }) => {
            store._m.forEach((misses, address) => {
                if (node.at(address) === child) {
                    misses.forEach(([messages, clear]) => {
                        child.store.apply(messages, clear);
                    });
                    store._m.delete(address);
                }
            });
            // If all the stored misses were applied, remove the listener.
            if (store._m.size === 0 && store._r) {
                node.off(store._r);
                store._r = undefined;
            }
        });
    }
    /**
     * Iterates over all buffered messages and applies them in sequence.
     *
     * @param messageStore - The store itself
     * @param store - The store interface
     * @param node - The node to filter for
     *
     * @internal
     */
    function releaseBuffer(_messageStore, store) {
        store.buffer = false;
        store._b.forEach(([messages, clear]) => store.apply(messages, clear));
        store._b = [];
    }

    /**
     * Creates a new ledger for use on a single node's context.
     *
     * @internal
     */
    function createLedger() {
        const ledger = {};
        let n;
        return {
            count: (...args) => createCounter(n, ledger, ...args),
            init(node) {
                n = node;
                node.on('message-added.deep', add(ledger, 1));
                node.on('message-removed.deep', add(ledger, -1));
            },
            merge: (child) => merge(n, ledger, child),
            settled(counterName) {
                return has(ledger, counterName)
                    ? ledger[counterName].promise
                    : Promise.resolve();
            },
            unmerge: (child) => merge(n, ledger, child, true),
            value(counterName) {
                return has(ledger, counterName) ? ledger[counterName].count : 0;
            },
        };
    }
    /**
     * Creates a new counter object in the counting ledger.
     *
     * @param node - FormKitNode
     * @param ledger - The actual ledger storage object
     * @param counterName - The name of the counter, can be arbitrary
     * @param condition - The condition function (or string) that filters messages
     * @param increment - The increment value
     *
     * @internal
     */
    function createCounter(node, ledger, counterName, condition, increment = 0) {
        condition = parseCondition(condition || counterName);
        if (!has(ledger, counterName)) {
            const counter = {
                condition,
                count: 0,
                name: counterName,
                node,
                promise: Promise.resolve(),
                resolve: () => { }, // eslint-disable-line @typescript-eslint/no-empty-function
            };
            ledger[counterName] = counter;
            increment = node.store.reduce((sum, m) => sum + counter.condition(m) * 1, increment);
            node.each((child) => {
                child.ledger.count(counter.name, counter.condition);
                increment += child.ledger.value(counter.name);
            });
        }
        return count(ledger[counterName], increment).promise;
    }
    /**
     * We parse the condition to allow flexibility in how counters are specified.
     *
     * @param condition - The condition that, if true, allows a message to change a counter's value
     *
     * @internal
     */
    function parseCondition(condition) {
        if (typeof condition === 'function') {
            return condition;
        }
        return (m) => m.type === condition;
    }
    /**
     * Perform a counting action on the a given counter object of the ledger.
     *
     * @param counter - A counter object
     * @param increment - The amount by which we are changing the count value
     *
     * @internal
     */
    function count(counter, increment) {
        const initial = counter.count;
        const post = counter.count + increment;
        counter.count = post;
        if (initial === 0 && post !== 0) {
            counter.node.emit(`unsettled:${counter.name}`, counter.count, false);
            counter.promise = new Promise((r) => (counter.resolve = r));
        }
        else if (initial !== 0 && post === 0) {
            counter.node.emit(`settled:${counter.name}`, counter.count, false);
            counter.resolve();
        }
        counter.node.emit(`count:${counter.name}`, counter.count, false);
        return counter;
    }
    /**
     * Returns a function to be used as an event listener for message events.
     *
     * @param ledger - A ledger to operate on
     * @param delta - The amount to add or subtract
     *
     * @internal
     */
    function add(ledger, delta) {
        return (e) => {
            for (const name in ledger) {
                const counter = ledger[name];
                if (counter.condition(e.payload)) {
                    count(counter, delta);
                }
            }
        };
    }
    /**
     * Given a child node, add the parent node's counters to the child and then
     * rectify the upstream ledger counts. Generally used when attaching a child
     * to an already counted tree.
     *
     * @param parent - The parent that is "receiving" the child
     * @param ledger - The ledger object
     * @param child - The child (can be a subtree) that is being attached
     * @param remove - If the merge is removing instead of adding
     *
     * @internal
     */
    function merge(parent, ledger, child, remove = false) {
        for (const key in ledger) {
            const condition = ledger[key].condition;
            if (!remove)
                child.ledger.count(key, condition);
            const increment = child.ledger.value(key) * (remove ? -1 : 1);
            if (!parent)
                continue;
            do {
                parent.ledger.count(key, condition, increment);
                parent = parent.parent;
            } while (parent);
        }
    }

    /**
     * A global registry of nodes by their alias or name (if root).
     */
    const registry = new Map();
    const reflected = new Map();
    /**
     * An event emitter for registered/set/unset nodes
     */
    const emit = createEmitter();
    /**
     * Receipts of listeners.
     */
    const receipts = [];
    /**
     * Registers a node to the registry _if_ the node is a root node, _or_ if the
     * node has an explicit node.props.alias. If these two things are not true,
     * then no node is registered (idempotent).
     *
     * @param node - A {@link FormKitNode | FormKitNode}.
     *
     * @public
     */
    function register(node) {
        if (node.props.id) {
            registry.set(node.props.id, node);
            reflected.set(node, node.props.id);
            emit(node, {
                payload: node,
                name: node.props.id,
                bubble: false,
                origin: node,
            });
        }
    }
    /**
     * Deregister a node from the registry.
     *
     * @param node - A {@link FormKitNode | FormKitNode}.
     *
     * @public
     */
    function deregister(node) {
        if (reflected.has(node)) {
            const id = reflected.get(node); // eslint-disable-line @typescript-eslint/no-non-null-assertion
            reflected.delete(node);
            registry.delete(id);
            emit(node, {
                payload: null,
                name: id,
                bubble: false,
                origin: node,
            });
        }
    }
    /**
     * Get a node by a particular id.
     *
     * @param id - Get a node by a given id.
     *
     * @returns A {@link FormKitNode | FormKitNode} or `undefined`.
     *
     * @public
     */
    function getNode$1(id) {
        return registry.get(id);
    }
    /**
     * A way of watching changes in the global registry.
     *
     * @param id - A dot-syntax id where the node is located.
     * @param callback - A callback in the format of {@link FormKitEventListener | FormKitEventListener} to notify when the node is set or removed.
     *
     * @public
     */
    function watchRegistry(id, callback) {
        // register a listener
        receipts.push(emit.on(id, callback));
    }

    /**
     * Applies a given config change to the node.
     *
     * @param node - The node to check for config change
     * @param prop - Checks if this property exists in the local config or props
     * @param value - The value to set
     *
     * @internal
     */
    function configChange(node, prop, value) {
        // When we return false, node.walk will not continue into that child.
        let usingFallback = true;
        !(prop in node.config._t)
            ? node.emit(`config:${prop}`, value, false)
            : (usingFallback = false);
        if (!(prop in node.props)) {
            node.emit('prop', { prop, value });
            node.emit(`prop:${prop}`, value);
        }
        return usingFallback;
    }
    /**
     * Creates a new instance of a global configuration option. This object is
     * essentially just a FormKitOption object, but it can be used as the root for
     * FormKitConfig's proxy and retain event "emitting".
     *
     * @param options - An object of optional properties of {@link FormKitConfig | FormKitConfig}.
     *
     * @returns A {@link FormKitRootConfig | FormKitRootConfig}.
     *
     * @public
     */
    function createConfig$1(options = {}) {
        const nodes = new Set();
        const target = {
            ...options,
            ...{
                _add: (node) => nodes.add(node),
                _rm: (node) => nodes.delete(node),
            },
        };
        const rootConfig = new Proxy(target, {
            set(t, prop, value, r) {
                if (typeof prop === 'string') {
                    nodes.forEach((node) => configChange(node, prop, value));
                }
                return Reflect.set(t, prop, value, r);
            },
        });
        return rootConfig;
    }

    /**
     * Submits a FormKit form programmatically.
     *
     * @param id - The id of the form.
     *
     * @public
     */
    function submitForm(id) {
        const formElement = document.getElementById(id);
        if (formElement instanceof HTMLFormElement) {
            const event = new Event('submit', { cancelable: true, bubbles: true });
            formElement.dispatchEvent(event);
            return;
        }
        warn(151, id);
    }

    /**
     * Clear all state and error messages.
     *
     * @internal
     */
    function clearState(node) {
        const clear = (n) => {
            for (const key in n.store) {
                const message = n.store[key];
                if (message.type === 'error' ||
                    (message.type === 'ui' && key === 'incomplete')) {
                    n.store.remove(key);
                }
                else if (message.type === 'state') {
                    n.store.set({ ...message, value: false });
                }
            }
        };
        clear(node);
        node.walk(clear);
    }
    /**
     * Resets an input to its "initial" value. If the input is a group or list it
     * resets all the children as well.
     *
     * @param id - The id of an input to reset.
     * @param resetTo - A value to reset the node to.
     *
     * @returns A {@link FormKitNode | FormKitNode} or `undefined`.
     *
     * @public
     */
    function reset(id, resetTo) {
        const node = typeof id === 'string' ? getNode$1(id) : id;
        if (node) {
            const initial = (n) => cloneAny(n.props.initial) ||
                (n.type === 'group' ? {} : n.type === 'list' ? [] : undefined);
            // pause all events in this tree.
            node._e.pause(node);
            // Set it back to basics
            node.input(cloneAny(resetTo) || initial(node), false);
            // Set children back to basics in case they were additive (had their own value for example)
            node.walk((child) => child.input(initial(child), false));
            // Finally we need to lay any values back on top (if it is a group/list) since group values
            // take precedence over child values.
            const finalInit = initial(node);
            node.input(typeof finalInit === 'object'
                ? cloneAny(resetTo) || init(finalInit)
                : finalInit, false);
            // release the events.
            node._e.play(node);
            clearState(node);
            node.emit('reset', node);
            return node;
        }
        warn(152, id);
        return;
    }

    /**
     * Default configuration options.
     */
    const defaultConfig$1 = {
        delimiter: '.',
        delay: 0,
        locale: 'en',
        rootClasses: (key) => ({ [`formkit-${kebab(key)}`]: true }),
    };
    /**
     * If a node’s name is set to useIndex, it replaces the node’s name with the
     * index of the node relative to its parent’s children.
     *
     * @internal
     */
    const useIndex = Symbol('index');
    /**
     * When propagating values up a tree, this value indicates the child should be
     * removed.
     *
     * @internal
     */
    const valueRemoved = Symbol('removed');
    /**
     * When propagating values up a tree, this value indicates the child should be
     * moved.
     *
     * @internal
     */
    const valueMoved = Symbol('moved');
    /**
     * When creating a new node and having its value injected directly at a specific
     * location.
     *
     * @internal
     */
    const valueInserted = Symbol('inserted');
    /**
     * A simple type guard to determine if the context being evaluated is a list
     * type.
     *
     * @param arg - A {@link FormKitContextShape | FormKitContextShape}.
     *
     * @returns Returns a `boolean`.
     *
     * @public
     */
    function isList(arg) {
        return arg.type === 'list' && Array.isArray(arg._value);
    }
    /**
     * Determine if a given object is a node.
     *
     * @example
     *
     * ```javascript
     * import { isNode, createNode } from '@formkit/core'
     *
     * const input = createNode({
     *   type: 'input', // defaults to 'input' if not specified
     *   value: 'hello node world',
     * })
     *
     * const obj = {};
     *
     * isNode(obj)
     * // false
     *
     * isNode(input)
     * // true
     * ```
     *
     * @param node - Any value.
     *
     * @returns Returns a `boolean`.
     *
     * @public
     */
    // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
    function isNode(node) {
        return node && typeof node === 'object' && node.__FKNode__ === true;
    }
    /**
     * The setter you are trying to access is invalid.
     */
    const invalidSetter = (node, _context, property) => {
        error(102, [node, property]);
    };
    const traps = {
        _c: trap(getContext, invalidSetter, false),
        add: trap(addChild),
        addProps: trap(addProps),
        address: trap(getAddress, invalidSetter, false),
        at: trap(getNode),
        bubble: trap(bubble),
        clearErrors: trap(clearErrors$1),
        calm: trap(calm),
        config: trap(false),
        define: trap(define),
        disturb: trap(disturb),
        destroy: trap(destroy),
        hydrate: trap(hydrate),
        index: trap(getIndex, setIndex, false),
        input: trap(input),
        each: trap(eachChild),
        emit: trap(emit$1),
        find: trap(find),
        on: trap(on),
        off: trap(off),
        parent: trap(false, setParent),
        plugins: trap(false),
        remove: trap(removeChild),
        root: trap(getRoot, invalidSetter, false),
        reset: trap(resetValue),
        resetConfig: trap(resetConfig),
        setErrors: trap(setErrors$1),
        submit: trap(submit),
        t: trap(text$1),
        use: trap(use),
        name: trap(getName, false, false),
        walk: trap(walkTree),
    };
    /**
     * These are all the available "traps" for a given node. You can think of these
     * a little bit like methods, but they are really Proxy interceptors.
     */
    function createTraps() {
        return new Map(Object.entries(traps));
    }
    /**
     * Creates a getter/setter trap and curries the context/node pair
     *
     * @param getter - The getter function
     * @param setter - The setter function
     * @param curryGetter - Indicates if the getter should be curried or not
     *
     * @returns A {@link FormKitTrap | FormKitTrap}
     */
    function trap(getter, setter, curryGetter = true) {
        return {
            get: getter
                ? (node, context) => curryGetter
                    ? (...args) => getter(node, context, ...args)
                    : getter(node, context)
                : false,
            set: setter !== undefined ? setter : invalidSetter.bind(null),
        };
    }
    /**
     * Create all of the node's hook dispatchers.
     */
    function createHooks() {
        const hooks = new Map();
        return new Proxy(hooks, {
            get(_, property) {
                if (!hooks.has(property)) {
                    hooks.set(property, createDispatcher$1());
                }
                return hooks.get(property);
            },
        });
    }
    /**
     * This is a simple integer counter of every createName() where the name needs
     * to be generated.
     */
    let nameCount = 0;
    /**
     * This is a simple integer counter of every default id created.
     */
    let idCount = 0;
    /**
     * Resets the global number of node registrations, useful for deterministic
     * node naming.
     *
     * @public
     */
    function resetCount() {
        nameCount = 0;
        idCount = 0;
    }
    /**
     * This node is responsible for deterministically generating an id for this
     * node. This cannot just be a random id, it _must_ be deterministic to ensure
     * re-hydration of the form (like post-SSR) produces the same names/ids.
     *
     * @param options - A {@link FormKitOptions | FormKitOptions}
     *
     * @returns `string | symbol`
     *
     * @internal
     */
    function createName(options) {
        var _a, _b;
        if (((_a = options.parent) === null || _a === void 0 ? void 0 : _a.type) === 'list')
            return useIndex;
        return options.name || `${((_b = options.props) === null || _b === void 0 ? void 0 : _b.type) || 'input'}_${++nameCount}`;
    }
    /**
     * Creates the initial value for a node based on the options passed in and the
     * type of the input.
     *
     * @param options - A {@link FormKitOptions | FormKitOptions}.
     *
     * @returns `unknown`
     *
     * @public
     */
    function createValue(options) {
        if (options.type === 'group') {
            return init(options.value &&
                typeof options.value === 'object' &&
                !Array.isArray(options.value)
                ? options.value
                : {});
        }
        else if (options.type === 'list') {
            return init(Array.isArray(options.value) ? options.value : []);
        }
        return options.value;
    }
    /**
     * Sets the internal value of the node.
     *
     * @param node - A {@link FormKitNode | FormKitNode}
     * @param context - A {@link FormKitContext | FormKitContext}
     * @param value - A input value to the node
     * @param async - If its an async call
     *
     * @returns `Promise<unknown>`
     *
     * @internal
     */
    function input(node, context, value, async = true) {
        context._value = validateInput(node, node.hook.input.dispatch(value));
        node.emit('input', context._value);
        if (context.isSettled)
            node.disturb();
        if (async) {
            if (context._tmo)
                clearTimeout(context._tmo);
            context._tmo = setTimeout(commit, node.props.delay, node, context);
        }
        else {
            commit(node, context);
        }
        return context.settled;
    }
    /**
     * Validate that the current input is allowed.
     *
     * @param node - A {@link FormKitNode | FormKitNode}
     * @param value - The value that is being validated
     *
     * @returns `T`
     *
     * @internal
     */
    function validateInput(node, value) {
        switch (node.type) {
            // Inputs are allowed to have any type
            case 'input':
                break;
            case 'group':
                if (!value || typeof value !== 'object')
                    error(107, [node, value]);
                break;
            case 'list':
                if (!Array.isArray(value))
                    error(108, [node, value]);
                break;
        }
        return value;
    }
    /**
     * Commits the working value to the node graph as the value of this node.
     *
     * @param node - A {@link FormKitNode | FormKitNode}
     * @param context - A {@link FormKitContext | FormKitContext}
     * @param calm - If it calms the node
     * @param hydrate - If it hydrates the node
     *
     * @internal
     */
    function commit(node, context, calm = true, hydrate = true) {
        context._value = context.value = node.hook.commit.dispatch(context._value);
        if (node.type !== 'input' && hydrate)
            node.hydrate();
        node.emit('commit', context.value);
        if (calm)
            node.calm();
    }
    /**
     * Perform a modification to a single element of a parent aggregate value. This
     * is only performed on the pre-committed value (_value), although typically
     * the value and _value are both linked in memory.
     *
     * @param context - A {@link FormKitContext | FormKitContext}
     *
     * @internal
     */
    function partial(context, { name, value, from }) {
        if (Object.isFrozen(context._value))
            return;
        if (isList(context)) {
            const insert = value === valueRemoved
                ? []
                : value === valueMoved && typeof from === 'number'
                    ? context._value.splice(from, 1)
                    : [value];
            context._value.splice(name, value === valueMoved || from === valueInserted ? 0 : 1, ...insert);
            return;
        }
        // In this case we know for sure we're dealing with a group, TS doesn't
        // know that however, so we use some unpleasant casting here
        if (value !== valueRemoved) {
            context._value[name] = value;
        }
        else {
            delete context._value[name];
        }
    }
    /**
     * Hydrate node and its children
     *
     * @param node - A {@link FormKitNode | FormKitNode}
     * @param context - A {@link FormKitContext | FormKitContext}
     *
     * @returns A {@link FormKitNode | FormKitNode}
     *
     * @internal
     */
    function hydrate(node, context) {
        const _value = context._value;
        context.children.forEach((child) => {
            if (typeof _value !== 'object')
                return;
            // if (has(context._value as FormKitGroupValue, child.name)) {
            if (child.name in _value) {
                // In this case, the parent has a value to give to the child, so we
                // perform a down-tree synchronous input which will cascade values down
                // and then ultimately back up.
                const childValue = child.type !== 'input' ||
                    (_value[child.name] && typeof _value[child.name] === 'object')
                    ? init(_value[child.name])
                    : _value[child.name];
                child.input(childValue, false);
            }
            else {
                if (node.type !== 'list' || typeof child.name === 'number') {
                    // In this case, the parent’s values have no knowledge of the child
                    // value — this typically occurs on the commit at the end of addChild()
                    // we need to create a value reservation for this node’s name. This is
                    // especially important when dealing with lists where index matters.
                    partial(context, { name: child.name, value: child.value });
                }
                if (!_value.__init) {
                    // In this case, someone has explicitly set the value to an empty object
                    // with node.input({}) so we do not define the __init property:
                    if (child.type === 'group')
                        child.input({}, false);
                    else if (child.type === 'list')
                        child.input([], false);
                    else
                        child.input(undefined, false);
                }
            }
        });
        return node;
    }
    /**
     * Disturbs the state of a node from settled to unsettled — creating appropriate
     * promises and resolutions.
     *
     * @param node - A {@link FormKitNode | FormKitNode}
     * @param context - A {@link FormKitContext | FormKitContext}
     *
     * @returns A {@link FormKitNode | FormKitNode}
     *
     * @internal
     */
    function disturb(node, context) {
        var _a;
        if (context._d <= 0) {
            context.isSettled = false;
            node.emit('settled', false, false);
            context.settled = new Promise((resolve) => {
                context._resolve = resolve;
            });
            if (node.parent)
                (_a = node.parent) === null || _a === void 0 ? void 0 : _a.disturb();
        }
        context._d++;
        return node;
    }
    /**
     * Calms the given node's disturbed state by one.
     *
     * @param node - A {@link FormKitNode | FormKitNode}
     * @param context - A {@link FormKitContext | FormKitContext}
     * @param value - A {@link FormKitChildValue | FormKitChildValue}
     *
     * @internal
     */
    function calm(node, context, value) {
        var _a;
        if (value !== undefined && node.type !== 'input') {
            partial(context, value);
            // Commit the value up, but do not hydrate back down
            return commit(node, context, true, false);
        }
        if (context._d > 0)
            context._d--;
        if (context._d === 0) {
            context.isSettled = true;
            node.emit('settled', true, false);
            if (node.parent)
                (_a = node.parent) === null || _a === void 0 ? void 0 : _a.calm({ name: node.name, value: context.value });
            if (context._resolve)
                context._resolve(context.value);
        }
    }
    /**
     * This node is being removed and needs to be cleaned up.
     *
     * @param node - A {@link FormKitNode | FormKitNode}
     * @param context - A {@link FormKitContext | FormKitContext}
     *
     * @internal
     */
    function destroy(node, context) {
        node.emit('destroying', node);
        // flush all messages out
        node.store.filter(() => false);
        if (node.parent) {
            node.parent.emit('childRemoved', node);
            node.parent.remove(node);
        }
        deregister(node);
        context._value = context.value = undefined;
        node.emit('destroyed', node);
    }
    /**
     * Defines the current input type concretely.
     *
     * @param node - A {@link FormKitNode | FormKitNode}
     * @param context - A {@link FormKitContext | FormKitContext}
     * @param definition - A {@link FormKitTypeDefinition | FormKitTypeDefinition}
     *
     * @internal
     */
    function define(node, context, definition) {
        // Assign the type
        context.type = definition.type;
        // Assign the definition
        context.props.definition = clone(definition);
        // Ensure the type is seeded with the `__init` value.
        context.value = context._value = createValue({
            type: node.type,
            value: context.value,
        });
        /**
         * If the user has a typename defined, use it here.
         */
        if (definition.forceTypeProp) {
            if (node.props.type)
                node.props.originalType = node.props.type;
            context.props.type = definition.forceTypeProp;
        }
        /**
         * If the input is part of a family of inputs, add that prop.
         */
        if (definition.family) {
            context.props.family = definition.family;
        }
        // Apply any input features before resetting the props.
        if (definition.features) {
            definition.features.forEach((feature) => feature(node));
        }
        // Its possible that input-defined "props" have ended up in the context attrs
        // these should be moved back out of the attrs object.
        if (definition.props) {
            node.addProps(definition.props);
        }
        node.emit('defined', definition);
    }
    /**
     * Adds props to a given node by stripping them out of the node.props.attrs and
     * then adding them to the nodes.
     *
     * @param node - A {@link FormKitNode | FormKitNode}
     * @param context - A {@link FormKitContext | FormKitContext}
     * @param props - An array of prop strings (in camelCase!)
     *
     * @returns A {@link FormKitNode | FormKitNode}
     *
     * @internal
     */
    function addProps(node, context, props) {
        var _a;
        if (node.props.attrs) {
            const attrs = { ...node.props.attrs };
            // Temporarily disable prop emits
            node.props._emit = false;
            for (const attr in attrs) {
                const camelName = camel(attr);
                if (props.includes(camelName)) {
                    node.props[camelName] = attrs[attr];
                    delete attrs[attr];
                }
            }
            const initial = cloneAny(context._value);
            node.props.initial =
                node.type !== 'input' ? init(initial) : initial;
            // Re-enable prop emits
            node.props._emit = true;
            node.props.attrs = attrs;
            if (node.props.definition) {
                node.props.definition.props = [
                    ...(((_a = node.props.definition) === null || _a === void 0 ? void 0 : _a.props) || []),
                    ...props,
                ];
            }
        }
        node.emit('added-props', props);
        return node;
    }
    /**
     * Adds a child to the node.
     *
     * @param node - A {@link FormKitNode | FormKitNode}
     * @param context - A parent {@link FormKitContext | FormKitContext}
     * @param child - A {@link FormKitNode | FormKitNode}
     * @param listIndex - A index number to be added at
     *
     * @internal
     */
    function addChild(parent, parentContext, child, listIndex) {
        if (parent.type === 'input')
            error(100, parent);
        if (child.parent && child.parent !== parent) {
            child.parent.remove(child);
        }
        // Synchronously set the initial value on the parent
        if (!parentContext.children.includes(child)) {
            if (listIndex !== undefined && parent.type === 'list') {
                // Inject the child:
                parentContext.children.splice(listIndex, 0, child);
                if (Array.isArray(parent.value) &&
                    parent.value.length < parentContext.children.length) {
                    // When adding an node or value to a list it is absolutely critical to
                    // know if, at the moment of injection, the parent’s value or the node
                    // children are the source of truth. For example, if a user pushes or
                    // splices a new value onto the lists’s array then we want to use that
                    // value as the value of the new node, but if a user adds a node to the
                    // list then we want the node’s value. In this specific case, we
                    // assume (due to length) that a new node was injected into the list, so
                    // we want that new node’s value injected into the parent list value.
                    parent.disturb().calm({
                        name: listIndex,
                        value: child.value,
                        from: valueInserted,
                    });
                }
            }
            else {
                parentContext.children.push(child);
            }
            if (!child.isSettled)
                parent.disturb();
        }
        if (child.parent !== parent) {
            child.parent = parent;
            // In this edge case middleware changed the parent assignment so we need to
            // re-add the child
            if (child.parent !== parent) {
                parent.remove(child);
                child.parent.add(child);
                return parent;
            }
        }
        else {
            // When a parent is properly assigned, we inject the parent's plugins on the
            // child.
            child.use(parent.plugins);
        }
        // Finally we call commit here, which sub-calls hydrate(), hydrate() will
        // resolve any conflict between the parent and child values, and also ensure
        // proper "placeholders" are made on the parent.
        commit(parent, parentContext, false);
        parent.ledger.merge(child);
        parent.emit('child', child);
        return parent;
    }
    /**
     * The setter for node.parent = FormKitNode
     * @param child - A child {@link FormKitNode | FormKitNode}
     * @param context - A {@link FormKitContext | FormKitContext}
     * @param _property - A property to be setted
     * @param parent - A parent {@link FormKitNode | FormKitNode}
     *
     * @returns `boolean`
     *
     * @internal
     */
    function setParent(child, context, _property, parent) {
        if (isNode(parent)) {
            if (child.parent && child.parent !== parent) {
                child.parent.remove(child);
            }
            context.parent = parent;
            child.resetConfig();
            !parent.children.includes(child)
                ? parent.add(child)
                : child.use(parent.plugins);
            return true;
        }
        if (parent === null) {
            context.parent = null;
            return true;
        }
        return false;
    }
    /**
     * Removes a child from the node.
     *
     * @param node - A {@link FormKitNode | FormKitNode}
     * @param context - A {@link FormKitContext | FormKitContext}
     * @param child - A child {@link FormKitNode | FormKitNode}
     *
     * @internal
     */
    function removeChild(node, context, child) {
        const childIndex = context.children.indexOf(child);
        if (childIndex !== -1) {
            if (child.isSettled)
                node.disturb();
            context.children.splice(childIndex, 1);
            // If an ancestor uses the preserve prop, then we are expected to not remove
            // our values on this node either, see #53
            let preserve = undefine(child.props.preserve);
            let parent = child.parent;
            while (preserve === undefined && parent) {
                preserve = undefine(parent.props.preserve);
                parent = parent.parent;
            }
            if (!preserve) {
                node.calm({
                    name: node.type === 'list' ? childIndex : child.name,
                    value: valueRemoved,
                });
            }
            else {
                node.calm();
            }
            child.parent = null;
            // Remove the child from the config. Is this weird? Yes. Is it ok? Yes.
            child.config._rmn = child;
        }
        node.ledger.unmerge(child);
        return node;
    }
    /**
     * Iterate over each immediate child and perform a callback.
     *
     * @param _node - A {@link FormKitNode | FormKitNode}
     * @param context - A {@link FormKitContext | FormKitContext}
     * @param callback - A {@link FormKitChildCallback | FormKitChildCallback}
     *
     * @internal
     */
    function eachChild(_node, context, callback) {
        context.children.forEach((child) => callback(child));
    }
    /**
     * Walk all nodes below this one and execute a callback.
     *
     * @param _node - A {@link FormKitNode | FormKitNode}
     * @param context - A {@link FormKitContext | FormKitContext}
     * @param callback - A {@link FormKitChildCallback | FormKitChildCallback}
     * @param stopIfFalse - Boolean to stop running on children
     *
     * @internal
     */
    function walkTree(_node, context, callback, stopIfFalse = false) {
        context.children.forEach((child) => {
            if (callback(child) !== false || !stopIfFalse) {
                child.walk(callback, stopIfFalse);
            }
        });
    }
    /**
     * Set the configuration options of the node and it's subtree.
     *
     * @param node - A {@link FormKitNode | FormKitNode}
     * @param context - A {@link FormKitContext | FormKitContext}
     *
     * @internal
     */
    function resetConfig(node, context) {
        const parent = node.parent || undefined;
        context.config = createConfig(node.config._t, parent);
        node.walk((n) => n.resetConfig());
    }
    /**
     * Adds a plugin to the node, its children, and executes it.
     *
     * @param node - A {@link FormKitNode | FormKitNode}
     * @param context - A {@link FormKitContext | FormKitContext}
     * @param plugin -
     * {@link FormKitPlugin | FormKitPlugin}
     * {@link FormKitPlugin | FormKitPlugin[]}
     * {@link FormKitPlugin | Set<FormKitPlugin>}
     * @param run - If it will run on creation
     * @param library - If it will run on library creation
     *
     * @returns A {@link FormKitNode | FormKitNode}
     *
     * @internal
     */
    function use(node, context, plugin, run = true, library = true) {
        if (Array.isArray(plugin) || plugin instanceof Set) {
            plugin.forEach((p) => use(node, context, p));
            return node;
        }
        if (!context.plugins.has(plugin)) {
            if (library && typeof plugin.library === 'function')
                plugin.library(node);
            // When plugins return false, they are never added as to the plugins Set
            // meaning they only ever have access to the single node they were added on.
            if (run && plugin(node) !== false) {
                context.plugins.add(plugin);
                node.children.forEach((child) => child.use(plugin));
            }
        }
        return node;
    }
    /**
     * Moves a node in the parent’s children to the given index.
     *
     * @param node - A {@link FormKitNode | FormKitNode}
     * @param _context - A {@link FormKitContext | FormKitContext}
     * @param _property - A property to add
     * @param setIndex - The index to move the node
     *
     * @internal
     */
    function setIndex(node, _context, _property, setIndex) {
        if (isNode(node.parent)) {
            const children = node.parent.children;
            const index = setIndex >= children.length
                ? children.length - 1
                : setIndex < 0
                    ? 0
                    : setIndex;
            const oldIndex = children.indexOf(node);
            if (oldIndex === -1)
                return false;
            children.splice(oldIndex, 1);
            children.splice(index, 0, node);
            node.parent.children = children;
            if (node.parent.type === 'list')
                node.parent
                    .disturb()
                    .calm({ name: index, value: valueMoved, from: oldIndex });
            return true;
        }
        return false;
    }
    /**
     * Retrieves the index of a node from the parent’s children.
     *
     * @param node - A {@link FormKitNode | FormKitNode}
     *
     * @internal
     */
    function getIndex(node) {
        if (node.parent) {
            const index = [...node.parent.children].indexOf(node);
            // If the index is currently -1 then the node isnt finished booting, so it
            // must be the next node.
            return index === -1 ? node.parent.children.length : index;
        }
        return -1;
    }
    /**
     * Retrieves the context object of a given node. This is intended to be a
     * private trap and should absolutely not be used in plugins or user-land code.
     *
     * @param _node - A {@link FormKitNode | FormKitNode}
     * @param context - A {@link FormKitContext | FormKitContext}
     *
     * @internal
     */
    function getContext(_node, context) {
        return context;
    }
    /**
     * Get the name of the current node, allowing for slight mutations.
     *
     * @param node - A {@link FormKitNode | FormKitNode}
     * @param context - A {@link FormKitContext | FormKitContext}
     *
     * @internal
     */
    function getName(node, context) {
        var _a;
        if (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === 'list')
            return node.index;
        return context.name !== useIndex ? context.name : node.index;
    }
    /**
     * Returns the address of the current node.
     *
     * @param node - A {@link FormKitNode | FormKitNode}
     * @param context - A {@link FormKitContext | FormKitContext}
     *
     * @internal
     */
    function getAddress(node, context) {
        return context.parent
            ? context.parent.address.concat([node.name])
            : [node.name];
    }
    /**
     * Fetches a node from the tree by its address.
     *
     * @param node - A {@link FormKitNode | FormKitNode}
     * @param _context - A {@link FormKitContext | FormKitContext}
     * @param locator - A string or {@link FormKitAddress | FormKitAddress} to find in the tree.
     *
     * @returns A {@link FormKitNode | FormKitNode}
     *
     * @internal
     */
    function getNode(node, _context, locator) {
        const address = typeof locator === 'string' ? locator.split(node.config.delimiter) : locator;
        if (!address.length)
            return undefined;
        const first = address[0];
        let pointer = node.parent;
        if (!pointer) {
            // This address names the root node, remove it to get child name:
            if (String(address[0]) === String(node.name))
                address.shift();
            // All root nodes start at themselves ultimately:
            pointer = node;
        }
        // Any addresses starting with $parent should discard it
        if (first === '$parent')
            address.shift();
        while (pointer && address.length) {
            const name = address.shift();
            switch (name) {
                case '$root':
                    pointer = node.root;
                    break;
                case '$parent':
                    pointer = pointer.parent;
                    break;
                case '$self':
                    pointer = node;
                    break;
                default:
                    pointer =
                        pointer.children.find((c) => String(c.name) === String(name)) ||
                            select$2(pointer, name);
            }
        }
        return pointer || undefined;
    }
    /**
     * Perform selections on a subtree using the address "selector" methods.
     *
     * @param node - A {@link FormKitNode | FormKitNode}
     * @param selector - A `string | number` to find in the node
     *
     * @returns A {@link FormKitNode | FormKitNode} or `undefined`
     *
     * @internal
     */
    function select$2(node, selector) {
        const matches = String(selector).match(/^(find)\((.*)\)$/);
        if (matches) {
            const [, action, argStr] = matches;
            const args = argStr.split(',').map((arg) => arg.trim());
            switch (action) {
                case 'find':
                    return node.find(args[0], args[1]);
                default:
                    return undefined;
            }
        }
        return undefined;
    }
    /**
     * Perform a breadth first search and return the first instance of a node that
     * is found in the subtree or undefined.
     *
     * @param node - A {@link FormKitNode | FormKitNode}
     * @param _context - A {@link FormKitContext | FormKitContext}
     * @param searchTerm - The term we are searching for
     * @param searcher - Either a key of {@link FormKitNode | FormKitNode}, or a {@link FormKitSearchFunction | FormKitSearchFunction}
     *
     * @returns A {@link FormKitNode | FormKitNode} or `undefined`
     *
     * @internal
     */
    function find(node, _context, searchTerm, searcher) {
        return bfs(node, searchTerm, searcher);
    }
    /**
     * Perform a breadth-first search on a node subtree and locate the first
     * instance of a match.
     *
     * @param tree - A {@link FormKitNode | FormKitNode} to start from.
     * @param searchValue - A value to be searched.
     * @param searchGoal - A goal value.
     *
     * @returns A {@link FormKitNode | FormKitNode } or `undefined`.
     *
     * @public
     */
    function bfs(tree, searchValue, searchGoal = 'name') {
        const search = typeof searchGoal === 'string'
            ? (n) => n[searchGoal] == searchValue // non-strict comparison is intentional
            : searchGoal;
        const stack = [tree];
        while (stack.length) {
            const node = stack.shift(); // eslint-disable-line @typescript-eslint/no-non-null-assertion
            if (search(node, searchValue))
                return node;
            stack.push(...node.children);
        }
        return undefined;
    }
    /**
     * Get the root node of the tree.
     *
     * @param n - A {@link FormKitNode | FormKitNode}
     *
     * @internal
     */
    function getRoot(n) {
        let node = n;
        while (node.parent) {
            node = node.parent;
        }
        return node;
    }
    /**
     * Creates a new configuration option.
     *
     * @param target - An object of optional properties of {@link FormKitConfig | FormKitConfig}
     * @param parent - A parent {@link FormKitNode | FormKitNode}
     *
     * @returns {@link FormKitNode | FormKitNode}
     *
     * @internal
     */
    function createConfig(target = {}, parent) {
        let node = undefined;
        return new Proxy(target, {
            get(...args) {
                const prop = args[1];
                if (prop === '_t')
                    return target;
                const localValue = Reflect.get(...args);
                // Check our local values first
                if (localValue !== undefined)
                    return localValue;
                // Then check our parent values next
                if (parent) {
                    const parentVal = parent.config[prop];
                    if (parentVal !== undefined)
                        return parentVal;
                }
                if (target.rootConfig && typeof prop === 'string') {
                    const rootValue = target.rootConfig[prop];
                    if (rootValue !== undefined)
                        return rootValue;
                }
                // The default delay value should be 20
                if (prop === 'delay' && (node === null || node === void 0 ? void 0 : node.type) === 'input')
                    return 20;
                // Finally check the default values
                return defaultConfig$1[prop];
            },
            set(...args) {
                const prop = args[1];
                const value = args[2];
                if (prop === '_n') {
                    node = value;
                    if (target.rootConfig)
                        target.rootConfig._add(node);
                    return true;
                }
                if (prop === '_rmn') {
                    if (target.rootConfig)
                        target.rootConfig._rm(node);
                    node = undefined;
                    return true;
                }
                if (!eq(target[prop], value, false)) {
                    const didSet = Reflect.set(...args);
                    if (node) {
                        node.emit(`config:${prop}`, value, false);
                        configChange(node, prop, value);
                        // Walk the node tree and notify of config/prop changes where relevant
                        node.walk((n) => configChange(n, prop, value), true);
                    }
                    return didSet;
                }
                return true;
            },
        });
    }
    /**
     * Given a string of text, expose it for modification, translation, or full
     * replacement.

     * @param node - A {@link FormKitNode | FormKitNode}
     * @param _context - A {@link FormKitContext | FormKitContext}
     * @param key - A {@link FormKitTextFragment | FormKitTextFragment}, or generic string of text
     * @param type - A string to represent the text type
     *
     * @returns `string`
     *
     * @internal
     */
    function text$1(node, _context, key, type = 'ui') {
        const fragment = typeof key === 'string' ? { key, value: key, type } : key;
        const value = node.hook.text.dispatch(fragment);
        node.emit('text', value, false);
        return value.value;
    }
    /**
     * Submits the nearest ancestor that is a FormKit "form". It determines which
     * node is a form by locating an ancestor where node.props.isForm = true.
     *
     * @param node - A {@link FormKitNode | FormKitNode}
     *
     * @internal
     */
    function submit(node) {
        const name = node.name;
        do {
            if (node.props.isForm === true)
                break;
            if (!node.parent)
                error(106, name);
            node = node.parent;
        } while (node);
        if (node.props.id) {
            submitForm(node.props.id);
        }
    }
    /**
     * Reset to the original value.
     *
     * @param node - A {@link FormKitNode | FormKitNode}
     * @param _context - A {@link FormKitContext | FormKitContext}
     * @param value - The value to reset to
     *
     * @internal
     */
    function resetValue(node, _context, value) {
        return reset(node, value);
    }
    /**
     * Sets errors on the node and optionally its children.
     *
     * @param node - A {@link FormKitNode | FormKitNode}
     * @param _context - A {@link FormKitContext | FormKitContext}
     * @param localErrors - An array of {@link ErrorMessages | ErrorMessages} to set on this node
     * @param childErrors - An object of name of {@link ErrorMessages | ErrorMessages} to set on children.
     *
     * @internal
     */
    function setErrors$1(node, _context, localErrors, childErrors) {
        const sourceKey = `${node.name}-set`;
        const errors = node.hook.setErrors.dispatch({ localErrors, childErrors });
        createMessages(node, errors.localErrors, errors.childErrors).forEach((errors) => {
            node.store.apply(errors, (message) => message.meta.source === sourceKey);
        });
        return node;
    }
    /**
     * Clears errors on the node and optionally its children.
     *
     * @param node - A {@link FormKitNode | FormKitNode}
     * @param _context - A {@link FormKitContext | FormKitContext}
     * @param clearChildErrors - A boolean to clear children error or not.
     * @param sourceKey - The source key string to reset.
     *
     * @internal
     */
    function clearErrors$1(node, context, clearChildErrors = true, sourceKey) {
        setErrors$1(node, context, []);
        if (clearChildErrors) {
            sourceKey = sourceKey || `${node.name}-set`;
            node.walk((child) => {
                child.store.filter((message) => {
                    return !(message.type === 'error' &&
                        message.meta &&
                        message.meta.source === sourceKey);
                });
            });
        }
        return node;
    }
    /**
     * Middleware to assign default prop values as issued by core.
     *
     * @param node - A {@link FormKitNode | FormKitNode}
     *
     * @returns A {@link FormKitNode | FormKitNode}
     *
     * @internal
     */
    function defaultProps(node) {
        if (!has(node.props, 'id'))
            node.props.id = `input_${idCount++}`;
        return node;
    }
    /**
     * Create props based on initial values
     *
     * @param initial - An initial value to be transformed
     *
     * @internal
     */
    function createProps(initial) {
        const props = {
            initial: typeof initial === 'object' ? cloneAny(initial) : initial,
        };
        let node;
        let isEmitting = true;
        return new Proxy(props, {
            get(...args) {
                const [_t, prop] = args;
                if (has(props, prop))
                    return Reflect.get(...args);
                if (node && typeof prop === 'string' && node.config[prop] !== undefined)
                    return node.config[prop];
                return undefined;
            },
            set(target, property, originalValue, receiver) {
                if (property === '_n') {
                    node = originalValue;
                    return true;
                }
                if (property === '_emit') {
                    isEmitting = originalValue;
                    return true;
                }
                const { prop, value } = node.hook.prop.dispatch({
                    prop: property,
                    value: originalValue,
                });
                // Typescript compiler cannot handle a symbol index, even though js can:
                if (!eq(props[prop], value, false) ||
                    typeof value === 'object') {
                    const didSet = Reflect.set(target, prop, value, receiver);
                    if (isEmitting) {
                        node.emit('prop', { prop, value });
                        if (typeof prop === 'string')
                            node.emit(`prop:${prop}`, value);
                    }
                    return didSet;
                }
                return true;
            },
        });
    }
    /**
     * A cheap function that iterates over all plugins and stops once node.define
     * is called.
     *
     * @param node - A {@link FormKitNode | FormKitNode}
     * @param plugins - An array of {@link FormKitPlugin | FormKitPlugin}
     *
     * @internal
     */
    function findDefinition(node, plugins) {
        // If the definition is already there, force call to define.
        if (node.props.definition)
            return node.define(node.props.definition);
        for (const plugin of plugins) {
            if (node.props.definition)
                return;
            if (typeof plugin.library === 'function') {
                plugin.library(node);
            }
        }
    }
    /**
     * Create a new context object for our a FormKit node, given default information
     *
     * @param options - An options object of {@link FormKitOptions | FormKitOptions} to override the defaults.
     *
     * @returns A {@link FormKitContext | FormKitContext}
     *
     * @internal
     */
    function createContext(options) {
        const value = createValue(options);
        const config = createConfig(options.config || {}, options.parent);
        return {
            _d: 0,
            _e: createEmitter(),
            _resolve: false,
            _tmo: false,
            _value: value,
            children: dedupe(options.children || []),
            config,
            hook: createHooks(),
            isCreated: false,
            isSettled: true,
            ledger: createLedger(),
            name: createName(options),
            parent: options.parent || null,
            plugins: new Set(),
            props: createProps(value),
            settled: Promise.resolve(value),
            store: createStore(true),
            traps: createTraps(),
            type: options.type || 'input',
            value,
        };
    }
    /**
     * Initialize a node object's internal properties.
     *
     * @param node - A {@link FormKitNode | FormKitNode}
     * @param options - An options object of {@link FormKitOptions | FormKitOptions} to override the defaults.
     *
     * @returns A {@link FormKitNode | FormKitNode}
     *
     * @internal
     */
    function nodeInit(node, options) {
        var _a;
        // Set the internal node on the props, config, ledger and store
        node.ledger.init((node.store._n = node.props._n = node.config._n = node));
        // Apply given in options to the node.
        node.props._emit = false;
        if (options.props)
            Object.assign(node.props, options.props);
        node.props._emit = true;
        // Attempt to find a definition from the pre-existing plugins.
        findDefinition(node, new Set([
            ...(options.plugins || []),
            ...(node.parent ? node.parent.plugins : []),
        ]));
        // Then we apply each plugin's root code, we do this with an explicit loop
        // for that ity-bitty performance bump.
        if (options.plugins) {
            for (const plugin of options.plugins) {
                use(node, node._c, plugin, true, false);
            }
        }
        // Initialize the default props
        defaultProps(node);
        // Apply the parent to each child.
        node.each((child) => node.add(child));
        // If the node has a parent, ensure it's properly nested bi-directionally.
        if (node.parent)
            node.parent.add(node, options.index);
        // Inputs are leafs, and cannot have children
        if (node.type === 'input' && node.children.length)
            error(100, node);
        // Apply the input hook to the initial value.
        input(node, node._c, node._value, false);
        // Release the store buffer
        node.store.release();
        // Register the node globally if someone explicitly gave it an id
        if ((_a = options.props) === null || _a === void 0 ? void 0 : _a.id)
            register(node);
        // Our node is finally ready, emit it to the world
        node.emit('created', node);
        node.isCreated = true;
        return node;
    }
    /**
     * Creates a new instance of a FormKit Node. Nodes are the atomic unit of a FormKit graph.
     *
     * @example
     *
     * ```javascript
     * import { createNode } from '@formkit/core'
     *
     * const input = createNode({
     *   type: 'input', // defaults to 'input' if not specified
     *   value: 'hello node world',
     * })
     *
     * console.log(input.value)
     * // 'hello node world'
     * ```
     *
     * @param options - An options object of {@link FormKitOptions | FormKitOptions} to override the defaults.
     *
     * @returns A {@link @formkit/core#FormKitNode | FormKitNode}.
     *
     * @public
     */
    function createNode(options) {
        const ops = options || {};
        const context = createContext(ops);
        // Note: The typing for the proxy object cannot be fully modeled, thus we are
        // force-typing to a FormKitNode. See:
        // https://github.com/microsoft/TypeScript/issues/28067
        const node = new Proxy(context, {
            get(...args) {
                const [, property] = args;
                if (property === '__FKNode__')
                    return true;
                const trap = context.traps.get(property);
                if (trap && trap.get)
                    return trap.get(node, context);
                return Reflect.get(...args);
            },
            set(...args) {
                const [, property, value] = args;
                const trap = context.traps.get(property);
                if (trap && trap.set)
                    return trap.set(node, context, property, value);
                return Reflect.set(...args);
            },
        });
        return nodeInit(node, ops);
    }

    /**
     * Type narrow that a node is a DOM node.
     *
     * @param node - A schema node to check
     *
     * @returns `boolean`
     *
     * @public
     */
    function isDOM(node) {
        return typeof node !== 'string' && has(node, '$el');
    }
    /**
     * Type narrow that a node is a DOM node.
     *
     * @param node - A schema node to check.
     *
     * @returns `boolean`
     *
     * @public
     */
    function isComponent$1(node) {
        return typeof node !== 'string' && has(node, '$cmp');
    }
    /**
     * Root declaration.
     *
     * @param node - An object to check.
     */
    function isConditional(node) {
        if (!node || typeof node === 'string')
            return false;
        return has(node, 'if') && has(node, 'then');
    }
    /**
     * Determines if the node is syntactic sugar or not.
     *
     * @param node - A schema node to check.
     *
     * @returns `boolean`
     *
     * @public
     */
    function isSugar(node) {
        return typeof node !== 'string' && '$formkit' in node;
    }
    /**
     * Converts syntactic sugar nodes to standard nodes.
     *
     * @param node - A node to covert.
     *
     * @returns A {@link FormKitSchemaNode | FormKitSchemaNode} without the properties of {@link FormKitSchemaFormKit | FormKitSchemaFormKit}.
     *
     * @public
     */
    function sugar(node) {
        if (typeof node === 'string') {
            return {
                $el: 'text',
                children: node,
            };
        }
        if (isSugar(node)) {
            const { $formkit: type, for: iterator, if: condition, children, bind, ...props } = node;
            return Object.assign({
                $cmp: 'FormKit',
                props: { ...props, type },
            }, condition ? { if: condition } : {}, iterator ? { for: iterator } : {}, children ? { children } : {}, bind ? { bind } : {});
        }
        return node;
    }

    /**
     * Compiles a logical string like `"a != z || b == c"` into a single function.
     * The return value is an object with a "provide" method that iterates over all
     * requirement tokens to use as replacements.
     *
     * @example
     *
     * ```typescript
     * let name = {
     *   value: 'jon'
     * }
     * const condition = compile("$name == 'bob'").provide((token) => {
     *  return () => name.value // must return a function!
     * })
     *
     * condition() // false
     * ```
     *
     * @param expr - A string to compile.
     *
     * @returns A {@link FormKitCompilerOutput | FormKitCompilerOutput}.
     *
     * @public
     */
    function compile(expr) {
        /**
         * These tokens are replacements used in evaluating a given condition.
         */
        // const tokens: FormKitTokens = {}
        /**
         * The value of the provide() callback. Used for late binding.
         */
        let provideTokens;
        /**
         * These are token requirements like "$name.value" that are need to fulfill
         * a given condition call.
         */
        const requirements = new Set();
        /**
         * Expands the current value if it is a function.
         * @param operand - A left or right hand operand
         * @returns
         */
        const x = function expand(operand, tokens) {
            return typeof operand === 'function' ? operand(tokens) : operand;
        };
        /**
         * Comprehensive list of operators. This list MUST be
         * ordered by the length of the operator characters in descending order.
         */
        const operatorRegistry = [
            {
                '&&': (l, r, t) => x(l, t) && x(r, t),
                '||': (l, r, t) => x(l, t) || x(r, t),
            },
            {
                '===': (l, r, t) => !!(x(l, t) === x(r, t)),
                '!==': (l, r, t) => !!(x(l, t) !== x(r, t)),
                '==': (l, r, t) => !!(x(l, t) == x(r, t)),
                '!=': (l, r, t) => !!(x(l, t) != x(r, t)),
                '>=': (l, r, t) => !!(x(l, t) >= x(r, t)),
                '<=': (l, r, t) => !!(x(l, t) <= x(r, t)),
                '>': (l, r, t) => !!(x(l, t) > x(r, t)),
                '<': (l, r, t) => !!(x(l, t) < x(r, t)),
            },
            {
                '+': (l, r, t) => x(l, t) + x(r, t),
                '-': (l, r, t) => x(l, t) - x(r, t),
            },
            {
                '*': (l, r, t) => x(l, t) * x(r, t),
                '/': (l, r, t) => x(l, t) / x(r, t),
                '%': (l, r, t) => x(l, t) % x(r, t),
            },
        ];
        /**
         * A full list of all operator symbols.
         */
        const operatorSymbols = operatorRegistry.reduce((s, g) => {
            return s.concat(Object.keys(g));
        }, []);
        /**
         * An array of the first character of each operator.
         */
        const operatorChars = new Set(operatorSymbols.map((key) => key.charAt(0)));
        /**
         * Determines if the current character is the start of an operator symbol, if it
         * is, it returns that symbol.
         * @param symbols - An array of symbols that are considered operators
         * @param char - The current character being operated on
         * @param p - The position of the pointer
         * @param expression - The full string expression
         * @returns
         */
        function getOp(symbols, char, p, expression) {
            const candidates = symbols.filter((s) => s.startsWith(char));
            if (!candidates.length)
                return false;
            return candidates.find((symbol) => {
                if (expression.length >= p + symbol.length) {
                    const nextChars = expression.substring(p, p + symbol.length);
                    if (nextChars === symbol)
                        return symbol;
                }
                return false;
            });
        }
        /**
         * Determines the step number of the right or left hand operator.
         * @param p - The position of the pointer
         * @param expression - The full string expression
         * @param direction - 1 = right, 0 = left
         */
        function getStep(p, expression, direction = 1) {
            let next = direction
                ? expression.substring(p + 1).trim()
                : expression.substring(0, p).trim();
            if (!next.length)
                return -1;
            if (!direction) {
                // left hand direction could include a function name we need to remove
                const reversed = next.split('').reverse();
                const start = reversed.findIndex((char) => operatorChars.has(char));
                next = reversed.slice(start).join('');
            }
            const char = next[0];
            return operatorRegistry.findIndex((operators) => {
                const symbols = Object.keys(operators);
                return !!getOp(symbols, char, 0, next);
            });
        }
        /**
         * Extracts a tail call. For example:
         * ```
         * $foo().bar(baz) + 7
         * ```
         * Would extract "bar(baz)" and return p of 15 (after the (baz)).
         *
         * @param p - The position of a closing parenthetical.
         * @param expression - The full expression being parsed.
         */
        function getTail(pos, expression) {
            let tail = '';
            const length = expression.length;
            let depth = 0;
            for (let p = pos; p < length; p++) {
                const char = expression.charAt(p);
                if (char === '(') {
                    depth++;
                }
                else if (char === ')') {
                    depth--;
                }
                else if (depth === 0 && char === ' ') {
                    continue;
                }
                if (depth === 0 && getOp(operatorSymbols, char, p, expression)) {
                    return [tail, p - 1];
                }
                else {
                    tail += char;
                }
            }
            return [tail, expression.length - 1];
        }
        /**
         * Parse a string expression into a function that returns a boolean. This is
         * the magic behind schema logic like $if.
         * @param expression - A string expression to parse
         * @returns
         */
        function parseLogicals(expression, step = 0) {
            const operators = operatorRegistry[step];
            const length = expression.length;
            const symbols = Object.keys(operators);
            let depth = 0;
            let quote = false;
            let op = null;
            let operand = '';
            let left = null;
            let operation;
            let lastChar = '';
            let char = '';
            let parenthetical = '';
            let parenQuote = '';
            let startP = 0;
            const addTo = (depth, char) => {
                depth ? (parenthetical += char) : (operand += char);
            };
            for (let p = 0; p < length; p++) {
                lastChar = char;
                char = expression.charAt(p);
                if ((char === "'" || char === '"') &&
                    lastChar !== '\\' &&
                    ((depth === 0 && !quote) || (depth && !parenQuote))) {
                    if (depth) {
                        parenQuote = char;
                    }
                    else {
                        quote = char;
                    }
                    addTo(depth, char);
                    continue;
                }
                else if ((quote && (char !== quote || lastChar === '\\')) ||
                    (parenQuote && (char !== parenQuote || lastChar === '\\'))) {
                    addTo(depth, char);
                    continue;
                }
                else if (quote === char) {
                    quote = false;
                    addTo(depth, char);
                    continue;
                }
                else if (parenQuote === char) {
                    parenQuote = false;
                    addTo(depth, char);
                    continue;
                }
                else if (char === ' ') {
                    continue;
                }
                else if (char === '(') {
                    if (depth === 0) {
                        startP = p;
                    }
                    else {
                        parenthetical += char;
                    }
                    depth++;
                }
                else if (char === ')') {
                    depth--;
                    if (depth === 0) {
                        // Parenthetical statements cannot be grouped up in the implicit order
                        // of left/right statements based on which step they are on because
                        // they are parsed on every step and then must be applied to the
                        // operator. Example:
                        //
                        // 5 + (3) * 2
                        //
                        // This should yield 11 not 16. This order is normally implicit in the
                        // sequence of operators being parsed, but with parenthesis the parse
                        // happens each time. Instead we need to know if the resulting value
                        // should be applied to the left or the right hand operator. The
                        // general algorithm is:
                        //
                        // 1. Does this paren have an operator on the left or right side
                        // 2. If not, it's unnecessarily wrapped (3 + 2)
                        // 3. If it does, then which order of operation is highest?
                        // 4. Wait for the highest order of operation to bind to an operator.
                        // If the parenthetical has a preceding token like $fn(1 + 2) then we
                        // need to subtract the existing operand length from the start
                        // to determine if this is a left or right operation
                        const fn = typeof operand === 'string' && operand.startsWith('$')
                            ? operand
                            : undefined;
                        const hasTail = fn && expression.charAt(p + 1) === '.';
                        // It's possible the function has a chained tail call:
                        let tail = '';
                        if (hasTail) {
                            [tail, p] = getTail(p + 2, expression);
                        }
                        const lStep = op ? step : getStep(startP, expression, 0);
                        const rStep = getStep(p, expression);
                        if (lStep === -1 && rStep === -1) {
                            // This parenthetical was unnecessarily wrapped at the root, or
                            // these are args of a function call.
                            operand = evaluate(parenthetical, -1, fn, tail);
                        }
                        else if (op && (lStep >= rStep || rStep === -1) && step === lStep) {
                            // has a left hand operator with a higher order of operation
                            left = op.bind(null, evaluate(parenthetical, -1, fn, tail));
                            op = null;
                            operand = '';
                        }
                        else if (rStep > lStep && step === rStep) {
                            // should be applied to the right hand operator when it gets one
                            operand = evaluate(parenthetical, -1, fn, tail);
                        }
                        else {
                            operand += `(${parenthetical})${hasTail ? `.${tail}` : ''}`;
                        }
                        parenthetical = '';
                    }
                    else {
                        parenthetical += char;
                    }
                }
                else if (depth === 0 &&
                    (operation = getOp(symbols, char, p, expression))) {
                    if (p === 0) {
                        error(103, [operation, expression]);
                    }
                    // We identified the operator by looking ahead in the string, so we need
                    // our position to move past the operator
                    p += operation.length - 1;
                    if (p === expression.length - 1) {
                        error(104, [operation, expression]);
                    }
                    if (!op) {
                        // Bind the left hand operand
                        if (left) {
                            // In this case we've already parsed the left hand operator
                            op = operators[operation].bind(null, evaluate(left, step));
                            left = null;
                        }
                        else {
                            op = operators[operation].bind(null, evaluate(operand, step));
                            operand = '';
                        }
                    }
                    else if (operand) {
                        // Bind the right hand operand, and return the resulting expression as a new left hand operator
                        left = op.bind(null, evaluate(operand, step));
                        op = operators[operation].bind(null, left);
                        operand = '';
                    }
                    continue;
                }
                else {
                    addTo(depth, char);
                }
            }
            if (operand && op) {
                // If we were left with an operand after the loop, and an op, it should
                // be the right hand assignment.
                op = op.bind(null, evaluate(operand, step));
            }
            // If we don't have an op, but we do have a left hand assignment, then that
            // is actually our operator, so just re-assign it to op
            op = !op && left ? left : op;
            if (!op && operand) {
                // If we don't have any op but we do have an operand so there is no boolean
                // logic to perform, but that operand still means something so we need to
                // evaluate it and return it as a function
                op = (v, t) => {
                    return typeof v === 'function' ? v(t) : v;
                };
                op = op.bind(null, evaluate(operand, step));
            }
            if (!op && !operand) {
                error(105, expression);
            }
            return op;
        }
        /**
         * Given a string like '$name==bobby' evaluate it to true or false
         * @param operand - A left or right boolean operand — usually conditions
         * @param step - The current order of operation
         * @param fnToken - The token (string) representation of a function being called
         * @returns
         */
        function evaluate(operand, step, fnToken, tail //eslint-disable-line
        ) {
            if (fnToken) {
                const fn = evaluate(fnToken, operatorRegistry.length);
                let userFuncReturn;
                // "Tail calls" are dot accessors after a function $foo().value. We need
                // to compile tail calls, and then provide the function result to the
                // exposed tokens.
                let tailCall = tail
                    ? compile(`$${tail}`)
                    : false;
                if (typeof fn === 'function') {
                    const args = parseArgs(String(operand)).map((arg) => evaluate(arg, -1));
                    return (tokens) => {
                        const userFunc = fn(tokens);
                        if (typeof userFunc !== 'function') {
                            warn(150, fnToken);
                            return userFunc;
                        }
                        userFuncReturn = userFunc(...args.map((arg) => typeof arg === 'function' ? arg(tokens) : arg));
                        if (tailCall) {
                            tailCall = tailCall.provide((subTokens) => {
                                const rootTokens = provideTokens(subTokens);
                                const t = subTokens.reduce((tokenSet, token) => {
                                    const isTail = token === tail || (tail === null || tail === void 0 ? void 0 : tail.startsWith(`${token}(`));
                                    if (isTail) {
                                        const value = getAt(userFuncReturn, token);
                                        tokenSet[token] = () => value;
                                    }
                                    else {
                                        tokenSet[token] = rootTokens[token];
                                    }
                                    return tokenSet;
                                }, {});
                                return t;
                            });
                        }
                        return tailCall ? tailCall() : userFuncReturn;
                    };
                }
            }
            else if (typeof operand === 'string') {
                // the word true or false will never contain further operations
                if (operand === 'true')
                    return true;
                if (operand === 'false')
                    return false;
                if (operand === 'undefined')
                    return undefined;
                // Truly quotes strings cannot contain an operation, return the string
                if (isQuotedString(operand))
                    return rmEscapes(operand.substring(1, operand.length - 1));
                // Actual numbers cannot be contain an operation
                if (!isNaN(+operand))
                    return Number(operand);
                if (step < operatorRegistry.length - 1) {
                    return parseLogicals(operand, step + 1);
                }
                else {
                    if (operand.startsWith('$')) {
                        const cleaned = operand.substring(1);
                        requirements.add(cleaned);
                        return function getToken(tokens) {
                            return cleaned in tokens ? tokens[cleaned]() : undefined;
                        };
                    }
                    // In this case we are dealing with an unquoted string, just treat it
                    // as a plain string.
                    return operand;
                }
            }
            return operand;
        }
        /**
         * Compile the string.
         */
        const compiled = parseLogicals(expr.startsWith('$:') ? expr.substring(2) : expr);
        /**
         * Convert compiled requirements to an array.
         */
        const reqs = Array.from(requirements);
        /**
         * Provides token values via callback to compiled output.
         * @param callback - A callback that needs to provide all token requirements
         * @returns
         */
        function provide(callback) {
            provideTokens = callback;
            return Object.assign(compiled.bind(null, callback(reqs)), {
                provide,
            });
        }
        return Object.assign(compiled, {
            provide,
        });
    }

    /**
     * Function that produces a standardized object representation of CSS classes.
     *
     * @param propertyKey - the section key.
     * @param node - A {@link FormKitNode | FormKitNode}.
     * @param sectionClassList - A `string | Record<string, boolean>` or a {@link FormKitClasses | FormKitClasses}.
     *
     * @returns `Record<string, boolean>`
     *
     * @public
     */
    function createClasses(propertyKey, node, sectionClassList) {
        if (!sectionClassList)
            return {};
        if (typeof sectionClassList === 'string') {
            const classKeys = sectionClassList.split(' ');
            return classKeys.reduce((obj, key) => Object.assign(obj, { [key]: true }), {});
        }
        else if (typeof sectionClassList === 'function') {
            return createClasses(propertyKey, node, sectionClassList(node, propertyKey));
        }
        return sectionClassList;
    }
    /**
     * Combines multiple class lists into a single list.
     *
     * @param node - A {@link FormKitNode | FormKitNode}.
     * @param property - The property key to which the class list will be applied.
     * @param args - And array of `Record<string, boolean>` of CSS class list(s).
     *
     * @returns `string | null`
     *
     * @public
     */
    function generateClassList(node, property, ...args) {
        const combinedClassList = args.reduce((finalClassList, currentClassList) => {
            if (!currentClassList)
                return handleNegativeClasses(finalClassList);
            const { $reset, ...classList } = currentClassList;
            if ($reset) {
                return handleNegativeClasses(classList);
            }
            return handleNegativeClasses(Object.assign(finalClassList, classList));
        }, {});
        return Object.keys(node.hook.classes.dispatch({ property, classes: combinedClassList })
            .classes)
            .filter((key) => combinedClassList[key])
            .join(' ') || null;
    }
    function handleNegativeClasses(classList) {
        const removalToken = '$remove:';
        let hasNegativeClassValue = false;
        const applicableClasses = Object.keys(classList).filter((className) => {
            if (classList[className] && className.startsWith(removalToken)) {
                hasNegativeClassValue = true;
            }
            return classList[className];
        });
        if (applicableClasses.length > 1 && hasNegativeClassValue) {
            const negativeClasses = applicableClasses.filter(className => className.startsWith(removalToken));
            negativeClasses.map((negativeClass) => {
                const targetClass = negativeClass.substring(removalToken.length);
                classList[targetClass] = false;
                classList[negativeClass] = false;
            });
        }
        return classList;
    }

    /**
     * Sets errors on a form, group, or input.
     *
     * @param id - The id of a form.
     * @param localErrors - The errors to set on the form or the form’s inputs in
     * the format of {@link ErrorMessages | ErrorMessages}.
     * @param childErrors - (optional) The errors to set on the form or the form’s
     * inputs in the format of {@link ErrorMessages | ErrorMessages}.
     *
     * @public
     */
    function setErrors(id, localErrors, childErrors) {
        const node = getNode$1(id);
        if (node) {
            node.setErrors(localErrors, childErrors);
        }
        else {
            warn(651, id);
        }
    }
    /**
     * Clears errors on the node and optionally its children.
     *
     * @param id - The id of the node you want to clear errors for.
     * @param clearChildren - Determines if the children of this node should have
     * their errors cleared.
     *
     * @public
     */
    function clearErrors(id, clearChildren = true) {
        const node = getNode$1(id);
        if (node) {
            node.clearErrors(clearChildren);
        }
        else {
            warn(652, id);
        }
    }

    /**
     * The official FormKit core library. This package is responsible for most of FormKit’s internal functionality.
     * You can read documentation specifically on how it works at formkit.com.
     *
     * You can add this package by using `npm install @formkit/core` or `yarn add @formkit/core`.
     *
     * @packageDocumentation
     */
    /**
     * The current version of FormKit at the time the package is published. Is replaced
     * as part of the publishing script.
     *
     * @internal
     */
    const FORMKIT_VERSION = '__FKV__';

    /**
     * A registry of memoized schemas (in JSON) to their respective render function
     * and provider registry.
     */
    const memo = {};
    /**
     * This symbol represents the current component instance during render. It is
     * critical for linking the current instance to the data required for render.
     */
    let instanceKey;
    /**
     * A registry of scoped data produced during runtime that is keyed by the
     * instance symbol. For example data from: for-loop instances and slot data.
     */
    const instanceScopes = new Map();
    /**
     * Indicates the a section of the schema is raw.
     */
    const raw = '__raw__';
    /**
     * Is a class prop.
     */
    const isClassProp = /[a-zA-Z0-9\-][cC]lass$/;
    /**
     * Returns a reference as a placeholder to a specific location on an object.
     * @param data - A reactive data object
     * @param token - A dot-syntax string representing the object path
     * @returns
     */
    function getRef(token, data) {
        const value = vue.ref(null);
        if (token === 'get') {
            const nodeRefs = {};
            value.value = get$1.bind(null, nodeRefs);
            return value;
        }
        const path = token.split('.');
        vue.watchEffect(() => (value.value = getValue(data, path)));
        return value;
    }
    /**
     * Returns a value inside a set of data objects.
     * @param sets - An array of objects to search through
     * @param path - A array of string paths easily produced by split()
     * @returns
     */
    function getValue(set, path) {
        if (Array.isArray(set)) {
            for (const subset of set) {
                const value = subset !== false && getValue(subset, path);
                if (value !== undefined)
                    return value;
            }
            return undefined;
        }
        let foundValue = undefined;
        let obj = set;
        for (const i in path) {
            const key = path[i];
            if (typeof obj !== 'object' || obj === null) {
                foundValue = undefined;
                break;
            }
            const currentValue = obj[key];
            if (Number(i) === path.length - 1 && currentValue !== undefined) {
                // When the value is a function, we need to bind the `this` value
                // before providing this back to the compiler.
                foundValue =
                    typeof currentValue === 'function'
                        ? currentValue.bind(obj)
                        : currentValue;
                break;
            }
            obj = currentValue;
        }
        return foundValue;
    }
    /**
     * Get the node from the global registry
     * @param id - A dot-syntax string where the node is located.
     */
    function get$1(nodeRefs, id) {
        if (typeof id !== 'string')
            return warn(650);
        if (!(id in nodeRefs))
            nodeRefs[id] = vue.ref(undefined);
        if (nodeRefs[id].value === undefined) {
            nodeRefs[id].value = null;
            const root = getNode$1(id);
            if (root)
                nodeRefs[id].value = root.context;
            watchRegistry(id, ({ payload: node }) => {
                nodeRefs[id].value = isNode(node) ? node.context : node;
            });
        }
        return nodeRefs[id].value;
    }
    /**
     *
     * @param library - A library of concrete components to use
     * @param schema -
     * @returns
     */
    function parseSchema(library, schema) {
        /**
         * Given an if/then/else schema node, pre-compile the node and return the
         * artifacts for the render function.
         * @param data - The schema context object
         * @param library - The available components
         * @param node - The node to parse
         */
        function parseCondition(library, node) {
            const condition = provider(compile(node.if), { if: true });
            const children = createElements(library, node.then);
            const alternate = node.else ? createElements(library, node.else) : null;
            return [condition, children, alternate];
        }
        /**
         * Parses a conditional if/then/else attribute statement.
         * @param data - The data object
         * @param attr - The attribute
         * @param _default - The default value
         * @returns
         */
        function parseConditionAttr(attr, _default) {
            var _a, _b;
            const condition = provider(compile(attr.if));
            let b = () => _default;
            let a = () => _default;
            if (typeof attr.then === 'object') {
                a = parseAttrs(attr.then, undefined);
            }
            else if (typeof attr.then === 'string' && ((_a = attr.then) === null || _a === void 0 ? void 0 : _a.startsWith('$'))) {
                a = provider(compile(attr.then));
            }
            else {
                a = () => attr.then;
            }
            if (has(attr, 'else')) {
                if (typeof attr.else === 'object') {
                    b = parseAttrs(attr.else);
                }
                else if (typeof attr.else === 'string' && ((_b = attr.else) === null || _b === void 0 ? void 0 : _b.startsWith('$'))) {
                    b = provider(compile(attr.else));
                }
                else {
                    b = () => attr.else;
                }
            }
            return () => (condition() ? a() : b());
        }
        /**
         * Parse attributes for dynamic content.
         * @param attrs - Object of attributes
         * @returns
         */
        function parseAttrs(unparsedAttrs, bindExp, _default = {}) {
            const explicitAttrs = new Set(Object.keys(unparsedAttrs || {}));
            const boundAttrs = bindExp ? provider(compile(bindExp)) : () => ({});
            const staticAttrs = {};
            const setters = [
                (attrs) => {
                    const bound = boundAttrs();
                    for (const attr in bound) {
                        if (!explicitAttrs.has(attr)) {
                            attrs[attr] = bound[attr];
                        }
                    }
                },
            ];
            if (unparsedAttrs) {
                if (isConditional(unparsedAttrs)) {
                    // This is a root conditional object that must produce an object of
                    // attributes.
                    const condition = parseConditionAttr(unparsedAttrs, _default);
                    return condition;
                }
                // Some attributes are explicitly bound, we need to parse those ones
                // using the compiler and create a dynamic "setter".
                for (let attr in unparsedAttrs) {
                    const value = unparsedAttrs[attr];
                    let getValue;
                    const isStr = typeof value === 'string';
                    if (attr.startsWith(raw)) {
                        // attributes prefixed with __raw__ should not be parsed
                        attr = attr.substring(7);
                        getValue = () => value;
                    }
                    else if (isStr &&
                        value.startsWith('$') &&
                        value.length > 1 &&
                        !(value.startsWith('$reset') && isClassProp.test(attr))) {
                        // Most attribute values starting with $ should be compiled
                        // -class attributes starting with `$reset` should not be compiled
                        getValue = provider(compile(value));
                    }
                    else if (typeof value === 'object' && isConditional(value)) {
                        // Conditional attrs require further processing
                        getValue = parseConditionAttr(value, undefined);
                    }
                    else if (typeof value === 'object' && isPojo(value)) {
                        // Sub-parse pojos
                        getValue = parseAttrs(value);
                    }
                    else {
                        // In all other cases, the value is static
                        getValue = () => value;
                        staticAttrs[attr] = value;
                    }
                    setters.push((attrs) => {
                        attrs[attr] = getValue();
                    });
                }
            }
            return () => {
                const attrs = Array.isArray(unparsedAttrs) ? [] : {};
                setters.forEach((setter) => setter(attrs));
                return attrs;
            };
        }
        /**
         * Given a single schema node, parse it and extract the value.
         * @param data - A state object provided to each node
         * @param node - The schema node being parsed
         * @returns
         */
        function parseNode(library, _node) {
            let element = null;
            let attrs = () => null;
            let condition = false;
            let children = null;
            let alternate = null;
            let iterator = null;
            let resolve = false;
            const node = sugar(_node);
            if (isDOM(node)) {
                // This is an actual HTML DOM element
                element = node.$el;
                attrs =
                    node.$el !== 'text' ? parseAttrs(node.attrs, node.bind) : () => null;
            }
            else if (isComponent$1(node)) {
                // This is a Vue Component
                if (typeof node.$cmp === 'string') {
                    if (has(library, node.$cmp)) {
                        element = library[node.$cmp];
                    }
                    else {
                        element = node.$cmp;
                        resolve = true;
                    }
                }
                else {
                    // in this case it must be an actual component
                    element = node.$cmp;
                }
                attrs = parseAttrs(node.props, node.bind);
            }
            else if (isConditional(node)) {
                [condition, children, alternate] = parseCondition(library, node);
            }
            // This is the same as a "v-if" statement — not an if/else statement
            if (!isConditional(node) && 'if' in node) {
                condition = provider(compile(node.if));
            }
            else if (!isConditional(node) && element === null) {
                // In this odd case our element is actually a partial and
                // we only want to render the children.
                condition = () => true;
            }
            // Compile children down to a function
            if ('children' in node && node.children) {
                if (typeof node.children === 'string') {
                    // We are dealing with a raw string value
                    if (node.children.startsWith('$slots.')) {
                        // this is a lone text node, turn it into a slot
                        element = element === 'text' ? 'slot' : element;
                        children = provider(compile(node.children));
                    }
                    else if (node.children.startsWith('$') && node.children.length > 1) {
                        const value = provider(compile(node.children));
                        children = () => String(value());
                    }
                    else {
                        children = () => String(node.children);
                    }
                }
                else if (Array.isArray(node.children)) {
                    // We are dealing with node sub-children
                    children = createElements(library, node.children);
                }
                else {
                    // This is a conditional if/else clause
                    const [childCondition, c, a] = parseCondition(library, node.children);
                    children = (iterationData) => childCondition && childCondition()
                        ? c && c(iterationData)
                        : a && a(iterationData);
                }
            }
            if (isComponent$1(node)) {
                if (children) {
                    // Children of components need to be provided as an object of slots
                    // so we provide an object with the default slot provided as children.
                    // We also create a new scope for this default slot, and then on each
                    // render pass the scoped slot props to the scope.
                    const produceChildren = children;
                    children = (iterationData) => {
                        return {
                            default(slotData, key) {
                                var _a, _b, _c, _d;
                                // We need to switch the current instance key back to the one that
                                // originally called this component's render function.
                                const currentKey = instanceKey;
                                if (key)
                                    instanceKey = key;
                                if (slotData)
                                    (_a = instanceScopes.get(instanceKey)) === null || _a === void 0 ? void 0 : _a.unshift(slotData);
                                if (iterationData)
                                    (_b = instanceScopes.get(instanceKey)) === null || _b === void 0 ? void 0 : _b.unshift(iterationData);
                                const c = produceChildren(iterationData);
                                // Ensure our instance key never changed during runtime
                                if (slotData)
                                    (_c = instanceScopes.get(instanceKey)) === null || _c === void 0 ? void 0 : _c.shift();
                                if (iterationData)
                                    (_d = instanceScopes.get(instanceKey)) === null || _d === void 0 ? void 0 : _d.shift();
                                instanceKey = currentKey;
                                return c;
                            },
                        };
                    };
                    children.slot = true;
                }
                else {
                    // If we dont have any children, we still need to provide an object
                    // instead of an empty array (which raises a warning in vue)
                    children = () => ({});
                }
            }
            // Compile the for loop down
            if ('for' in node && node.for) {
                const values = node.for.length === 3 ? node.for[2] : node.for[1];
                const getValues = typeof values === 'string' && values.startsWith('$')
                    ? provider(compile(values))
                    : () => values;
                iterator = [
                    getValues,
                    node.for[0],
                    node.for.length === 3 ? String(node.for[1]) : null,
                ];
            }
            return [condition, element, attrs, children, alternate, iterator, resolve];
        }
        /**
         * Given a particular function that produces children, ensure that the second
         * argument of all these slots is the original instance key being used to
         * render the slots.
         * @param children - The children() function that will produce slots
         */
        function createSlots(children, iterationData) {
            const slots = children(iterationData);
            const currentKey = instanceKey;
            return Object.keys(slots).reduce((allSlots, slotName) => {
                const slotFn = slots && slots[slotName];
                allSlots[slotName] = (data) => {
                    return (slotFn && slotFn(data, currentKey)) || null;
                };
                return allSlots;
            }, {});
        }
        /**
         * Creates an element
         * @param data - The context data available to the node
         * @param node - The schema node to render
         * @returns
         */
        function createElement(library, node) {
            // Parses the schema node into pertinent parts
            const [condition, element, attrs, children, alternate, iterator, resolve] = parseNode(library, node);
            // This is a sub-render function (called within a render function). It must
            // only use pre-compiled features, and be organized in the most efficient
            // manner possible.
            let createNodes = ((iterationData) => {
                if (condition && element === null && children) {
                    // Handle conditional if/then statements
                    return condition()
                        ? children(iterationData)
                        : alternate && alternate(iterationData);
                }
                if (element && (!condition || condition())) {
                    // handle text nodes
                    if (element === 'text' && children) {
                        return vue.createTextVNode(String(children()));
                    }
                    // Handle lone slots
                    if (element === 'slot' && children)
                        return children(iterationData);
                    // Handle resolving components
                    const el = resolve ? vue.resolveComponent(element) : element;
                    // If we are rendering slots as children, ensure their instanceKey is properly added
                    const slots = (children === null || children === void 0 ? void 0 : children.slot)
                        ? createSlots(children, iterationData)
                        : null;
                    // Handle dom elements and components
                    return vue.h(el, attrs(), (slots || (children ? children(iterationData) : [])));
                }
                return typeof alternate === 'function'
                    ? alternate(iterationData)
                    : alternate;
            });
            if (iterator) {
                const repeatedNode = createNodes;
                const [getValues, valueName, keyName] = iterator;
                createNodes = (() => {
                    const _v = getValues();
                    const values = !isNaN(_v)
                        ? Array(Number(_v))
                            .fill(0)
                            .map((_, i) => i)
                        : _v;
                    const fragment = [];
                    if (typeof values !== 'object')
                        return null;
                    const instanceScope = instanceScopes.get(instanceKey) || [];
                    const isArray = Array.isArray(values);
                    for (const key in values) {
                        if (isArray && key in Array.prototype)
                            continue; // Fix #299
                        const iterationData = Object.defineProperty({
                            ...instanceScope.reduce((previousIterationData, scopedData) => {
                                if (previousIterationData.__idata) {
                                    return { ...previousIterationData, ...scopedData };
                                }
                                return scopedData;
                            }, {}),
                            [valueName]: values[key],
                            ...(keyName !== null
                                ? { [keyName]: isArray ? Number(key) : key }
                                : {}),
                        }, '__idata', { enumerable: false, value: true });
                        instanceScope.unshift(iterationData);
                        fragment.push(repeatedNode.bind(null, iterationData)());
                        instanceScope.shift();
                    }
                    return fragment;
                });
            }
            return createNodes;
        }
        /**
         * Given a schema, parse it and return the resulting renderable nodes.
         * @param data - The schema context object
         * @param library - The available components
         * @param node - The node to parse
         * @returns
         */
        function createElements(library, schema) {
            if (Array.isArray(schema)) {
                const els = schema.map(createElement.bind(null, library));
                return (iterationData) => els.map((element) => element(iterationData));
            }
            // Single node to render
            const element = createElement(library, schema);
            return (iterationData) => element(iterationData);
        }
        /**
         * Data providers produced as a result of the compiler.
         */
        const providers = [];
        /**
         * Append the requisite compiler provider and return the compiled function.
         * @param compiled - A compiled function
         * @returns
         */
        function provider(compiled, hints = {}) {
            const compiledFns = {};
            providers.push((callback, key) => {
                compiledFns[key] = compiled.provide((tokens) => callback(tokens, hints));
            });
            return () => compiledFns[instanceKey]();
        }
        /**
         * Creates a new instance of a given schema — this either comes from a
         * memoized copy of the parsed schema or a freshly parsed version. An symbol
         * instance key, and dataProvider functions are passed in.
         * @param providerCallback - A function that is called for each required provider
         * @param key - a symbol representing the current instance
         */
        return function createInstance(providerCallback, key) {
            const memoKey = JSON.stringify(schema);
            const [render, compiledProviders] = has(memo, memoKey)
                ? memo[memoKey]
                : [createElements(library, schema), providers];
            memo[memoKey] = [render, compiledProviders];
            compiledProviders.forEach((compiledProvider) => {
                compiledProvider(providerCallback, key);
            });
            return () => {
                instanceKey = key;
                return render();
            };
        };
    }
    /**
     * Checks the current runtime scope for data.
     * @param token - The token to lookup in the current scope
     * @param defaultValue - The default ref value to use if no scope is found.
     */
    function useScope(token, defaultValue) {
        const scopedData = instanceScopes.get(instanceKey) || [];
        let scopedValue = undefined;
        if (scopedData.length) {
            scopedValue = getValue(scopedData, token.split('.'));
        }
        return scopedValue === undefined ? defaultValue : scopedValue;
    }
    /**
     * Get the current scoped data and flatten it.
     */
    function slotData(data, key) {
        return new Proxy(data, {
            get(...args) {
                let data = undefined;
                const property = args[1];
                if (typeof property === 'string') {
                    const prevKey = instanceKey;
                    instanceKey = key;
                    data = useScope(property, undefined);
                    instanceKey = prevKey;
                }
                return data !== undefined ? data : Reflect.get(...args);
            },
        });
    }
    /**
     * Provides data to a parsed schema.
     * @param provider - The SchemaProvider (output of calling parseSchema)
     * @param data - Data to fetch values from
     * @returns
     */
    function createRenderFn(instanceCreator, data, instanceKey) {
        return instanceCreator((requirements, hints = {}) => {
            return requirements.reduce((tokens, token) => {
                if (token.startsWith('slots.')) {
                    const slot = token.substring(6);
                    const hasSlot = () => data.slots &&
                        has(data.slots, slot) &&
                        typeof data.slots[slot] === 'function';
                    if (hints.if) {
                        // If statement — dont render the slot, check if it exists
                        tokens[token] = hasSlot;
                    }
                    else if (data.slots) {
                        // Render the slot with current scope data
                        const scopedData = slotData(data, instanceKey);
                        tokens[token] = () => hasSlot() ? data.slots[slot](scopedData) : null;
                    }
                }
                else {
                    const value = getRef(token, data);
                    tokens[token] = () => useScope(token, value.value);
                }
                return tokens;
            }, {});
        }, instanceKey);
    }
    let i = 0;
    /**
     * The FormKitSchema vue component:
     *
     * @public
     */
    const FormKitSchema = vue.defineComponent({
        name: 'FormKitSchema',
        props: {
            schema: {
                type: [Array, Object],
                required: true,
            },
            data: {
                type: Object,
                default: () => ({}),
            },
            library: {
                type: Object,
                default: () => ({}),
            },
        },
        setup(props, context) {
            const instance = vue.getCurrentInstance();
            let instanceKey = Symbol(String(i++));
            instanceScopes.set(instanceKey, []);
            let provider = parseSchema(props.library, props.schema);
            let render;
            let data;
            // Re-parse the schema if it changes:
            vue.watch(() => props.schema, (newSchema, oldSchema) => {
                var _a;
                instanceKey = Symbol(String(i++));
                provider = parseSchema(props.library, props.schema);
                render = createRenderFn(provider, data, instanceKey);
                if (newSchema === oldSchema) {
                    ((_a = instance === null || instance === void 0 ? void 0 : instance.proxy) === null || _a === void 0 ? void 0 : _a.$forceUpdate)();
                }
            }, { deep: true });
            // Watch the data object explicitly
            vue.watchEffect(() => {
                data = Object.assign(vue.reactive(props.data), {
                    slots: context.slots,
                });
                render = createRenderFn(provider, data, instanceKey);
            });
            return () => render();
        },
    });

    /**
     * All the explicit FormKit props.
     */
    const nativeProps = {
        config: {
            type: Object,
            default: {},
        },
        classes: {
            type: Object,
            required: false,
        },
        delay: {
            type: Number,
            required: false,
        },
        errors: {
            type: Array,
            default: [],
        },
        inputErrors: {
            type: Object,
            default: () => ({}),
        },
        index: {
            type: Number,
            required: false,
        },
        id: {
            type: String,
            required: false,
        },
        modelValue: {
            required: false,
        },
        name: {
            type: String,
            required: false,
        },
        parent: {
            type: Object,
            required: false,
        },
        plugins: {
            type: Array,
            default: [],
        },
        sectionsSchema: {
            type: Object,
            default: {},
        },
        type: {
            type: [String, Object],
            default: 'text',
        },
        validation: {
            type: [String, Array],
            required: false,
        },
        validationMessages: {
            type: Object,
            required: false,
        },
        validationRules: {
            type: Object,
            required: false,
        },
        validationLabel: {
            type: [String, Function],
            required: false,
        },
    };
    /**
     * The FormKit props object.
     * @internal
     */
    const props = nativeProps;

    /**
     * The symbol that represents the formkit parent injection value.
     *
     * @public
     */
    const parentSymbol = Symbol('FormKitParent');
    /**
     * The root FormKit component.
     *
     * @public
     */
    const FormKit = vue.defineComponent({
        props,
        emits: {
            /* eslint-disable @typescript-eslint/no-unused-vars */
            input: (_value, _node) => true,
            inputRaw: (_value, _node) => true,
            'update:modelValue': (_value) => true,
            node: (node) => !!node,
            submit: (_data, _node) => true,
            submitRaw: (_event, _node) => true,
            submitInvalid: (_node) => true,
            /* eslint-enable @typescript-eslint/no-unused-vars */
        },
        inheritAttrs: false,
        setup(props, context) {
            const node = useInput(props, context);
            if (!node.props.definition)
                error(600, node);
            if (node.props.definition.component) {
                return () => {
                    var _a;
                    return vue.h((_a = node.props.definition) === null || _a === void 0 ? void 0 : _a.component, {
                        context: node.context,
                    }, { ...context.slots });
                };
            }
            const schema = vue.ref([]);
            const generateSchema = () => {
                var _a, _b;
                const schemaDefinition = (_b = (_a = node.props) === null || _a === void 0 ? void 0 : _a.definition) === null || _b === void 0 ? void 0 : _b.schema;
                if (!schemaDefinition)
                    error(601, node);
                schema.value =
                    typeof schemaDefinition === 'function'
                        ? schemaDefinition({ ...props.sectionsSchema })
                        : schemaDefinition;
            };
            generateSchema();
            // If someone emits the schema event, we re-generate the schema
            node.on('schema', generateSchema);
            context.emit('node', node);
            const library = node.props.definition.library;
            // Expose the FormKitNode to template refs.
            context.expose({ node });
            return () => vue.h(FormKitSchema, { schema: schema.value, data: node.context, library }, { ...context.slots });
        },
    });

    /**
     * The Create a new instance of the FormKit plugin for Vue.
     *
     * @param app - A Vue application
     * @param config - FormKit Vue plugin configuration options
     *
     * @internal
     */
    function createPlugin(app, options) {
        app
            .component(options.alias || 'FormKit', FormKit)
            .component(options.schemaAlias || 'FormKitSchema', FormKitSchema);
        return {
            get: getNode$1,
            setLocale: (locale) => {
                var _a;
                if ((_a = options.config) === null || _a === void 0 ? void 0 : _a.rootConfig) {
                    options.config.rootConfig.locale = locale;
                }
            },
            clearErrors,
            setErrors,
            submit: submitForm,
            reset,
        };
    }
    /**
     * The symbol key for accessing the FormKit node options.
     *
     * @public
     */
    const optionsSymbol = Symbol.for('FormKitOptions');
    /**
     * The symbol key for accessing FormKit root configuration.
     *
     * @public
     */
    const configSymbol = Symbol.for('FormKitConfig');
    /**
     * Create the FormKit plugin.
     *
     * @public
     */
    const plugin = {
        install(app, _options) {
            /**
             * Extend the default configuration options.
             */
            const options = Object.assign({
                alias: 'FormKit',
                schemaAlias: 'FormKitSchema',
            }, typeof _options === 'function' ? _options() : _options);
            /**
             * The root configuration options.
             */
            const rootConfig = createConfig$1(options.config || {});
            /**
             * We dont want to explicitly provide any "config" options, only a root
             * config option — so here we override the existing config options.
             */
            options.config = { rootConfig };
            /**
             * Register the global $formkit plugin property.
             */
            app.config.globalProperties.$formkit = createPlugin(app, options);
            /**
             * Provide the config to the application for injection.
             */
            app.provide(optionsSymbol, options);
            /**
             * Provide the root config to the application.
             */
            app.provide(configSymbol, rootConfig);
        },
    };

    /**
     * Indicates that the path that was requested is no longer valid in the object.
     */
    const invalidGet = Symbol();
    /**
     * A special watcher for Vue that reports the location of a deep mutation.
     *
     * @param obj - An object to observe at depth.
     * @param callback - A callback to call when a change is detected.
     *
     * @public
     */
    function watchVerbose(obj, callback) {
        const watchers = {};
        const applyWatch = (paths) => {
            // Watch each property
            for (const path of paths) {
                // Stops pre-existing watchers at a given location to prevent dupes:
                if (path.__str in watchers)
                    watchers[path.__str]();
                watchers[path.__str] = vue.watch(touch.bind(null, obj, path), dispatcher.bind(null, path), { deep: false });
            }
        };
        /**
         * Clear any watchers deeper than this path.
         * @param path - The path to start from
         */
        const clearWatch = (path) => {
            if (!path.length)
                return;
            for (const key in watchers) {
                if (`${key}`.startsWith(`${path.__str}.`)) {
                    watchers[key]();
                    delete watchers[key];
                }
            }
        };
        const dispatcher = createDispatcher(obj, callback, applyWatch, clearWatch);
        applyWatch(getPaths(obj));
    }
    /**
     * This function synchronously dispatches to the watch callbacks. It uses the
     * knowledge that the getPath function is a depth-first-search thus lower
     * specificity (lower tree nodes) will always have their watchers called first.
     * If a lower specificity watcher is triggered we want to ignore the higher
     * specificity watcher.
     * @param obj - The object to dispatch
     * @param callback - The callback function to emit
     * @param applyWatch - A way to apply watchers to update objects
     * @returns
     */
    function createDispatcher(obj, callback, applyWatch, clearChildWatches) {
        // let dispatchedPaths: Record<string, ObjectPath> = {}
        // let clear: Promise<void> | null = null
        return (path) => {
            const value = get(obj, path);
            if (value === invalidGet)
                return;
            if (path.__deep)
                clearChildWatches(path);
            if (typeof value === 'object')
                applyWatch(getPaths(value, [path], ...path));
            callback(path, value, obj);
        };
    }
    /**
     * "Touches" a given property for reactivity tracking purposes, if the value at
     * the given path is an object, we flatten it to just its keys since we will
     * already be tracking sub properties independently.
     * @param obj - A ref to traverse for a given path
     * @param path - An array of strings representing the path to locate
     * @returns
     */
    function touch(obj, path) {
        const value = get(obj, path);
        return value && typeof value === 'object' ? Object.keys(value) : value;
    }
    /**
     * "Touches" a given property for reactivity tracking purposes.
     * @param obj - A ref to traverse for a given path
     * @param path - An array of strings representing the path to locate
     * @returns
     */
    function get(obj, path) {
        if (vue.isRef(obj)) {
            if (path.length === 0)
                return obj.value;
            obj = obj.value;
        }
        return path.reduce((value, segment) => {
            if (value === invalidGet)
                return value;
            if (value === null || typeof value !== 'object') {
                return invalidGet;
            }
            return value[segment];
        }, obj);
    }
    /**
     * Recursively retrieves all enumerable property paths from the origination
     * object. For example:
     * ```js
     * const obj = {
     *   a: {
     *     b: 123
     *   },
     *   c: 567
     * }
     * const paths = getPaths(obj)
     * // [
     * //   ['a'],
     * //   ['a', 'b'],
     * //   ['c']
     * // ]
     * ```
     * @param obj - An object to retrieve paths for.
     * @param parents - An array of parent paths.
     * @returns
     * @internal
     */
    function getPaths(obj, paths = [], ...parents) {
        if (obj === null)
            return paths;
        if (!parents.length) {
            const path = Object.defineProperty([], '__str', {
                value: '',
            });
            obj = vue.isRef(obj) ? obj.value : obj;
            if (obj && typeof obj === 'object') {
                Object.defineProperty(path, '__deep', { value: true });
                paths.push(path);
            }
            else {
                return [path];
            }
        }
        if (obj === null || typeof obj !== 'object')
            return paths;
        for (const key in obj) {
            const path = parents.concat(key);
            Object.defineProperty(path, '__str', { value: path.join('.') });
            const value = obj[key];
            if (isPojo(value) || Array.isArray(value)) {
                paths.push(Object.defineProperty(path, '__deep', { value: true }));
                paths = paths.concat(getPaths(value, [], ...path));
            }
            else {
                paths.push(path);
            }
        }
        return paths;
    }

    /**
     * Gets the raw underlying target object from a Vue Ref or Reactive object.
     * @param obj - Get the underlying target object, or no-op.
     * @returns
     */
    // eslint-disable-next-line @typescript-eslint/ban-types
    function useRaw(obj) {
        if (obj === null || typeof obj !== 'object')
            return obj;
        if (vue.isReactive(obj)) {
            obj = vue.toRaw(obj);
        }
        else if (vue.isRef(obj)) {
            obj = (vue.isReactive(obj.value) ? useRaw(obj.value) : obj.value);
        }
        return obj;
    }

    /**
     * Props that are extracted from the attrs object.
     * TODO: Currently local, this should probably exported to a inputs or another
     * package.
     */
    const pseudoProps = [
        'help',
        'label',
        'ignore',
        'disabled',
        'preserve',
        /^preserve(-e|E)rrors/,
        /^[a-z]+(?:-visibility|Visibility)$/,
        /^[a-zA-Z-]+(?:-class|Class)$/,
        'prefixIcon',
        'suffixIcon',
        /^[a-zA-Z-]+(?:-icon|Icon)$/,
    ];
    /**
     * Given some props, map those props to individualized props internally.
     * @param node - A formkit node
     * @param props - Some props that may include a classes object
     */
    function classesToNodeProps(node, props) {
        if (props.classes) {
            Object.keys(props.classes).forEach((key) => {
                if (typeof key === 'string') {
                    node.props[`_${key}Class`] = props.classes[key];
                    // We need to ensure Vue is aware that we want to actually observe the
                    // child values too, so we touch them here.
                    if (isObject(props.classes[key]) && key === 'inner')
                        Object.values(props.classes[key]);
                }
            });
        }
    }
    /**
     * Extracts known FormKit listeners.
     * @param props - Extract known FormKit listeners.
     * @returns
     */
    function onlyListeners(props) {
        if (!props)
            return {};
        const knownListeners = ['Submit', 'SubmitRaw', 'SubmitInvalid'].reduce((listeners, listener) => {
            const name = `on${listener}`;
            if (name in props) {
                if (typeof props[name] === 'function') {
                    listeners[name] = props[name];
                }
            }
            return listeners;
        }, {});
        return knownListeners;
    }
    /**
     * A composable for creating a new FormKit node.
     *
     * @param type - The type of node (input, group, list)
     * @param attrs - The FormKit "props" — which is really the attrs list.
     *
     * @returns {@link @formkit/core#FormKitNode | FormKitNode}
     *
     * @public
     */
    function useInput(props, context, options = {}) {
        var _a;
        /**
         * The configuration options, these are provided by either the plugin or by
         * explicit props.
         */
        const config = Object.assign({}, vue.inject(optionsSymbol) || {}, options);
        /**
         * The current instance.
         */
        const instance = vue.getCurrentInstance();
        /**
         * Extracts the listeners.
         */
        const listeners = onlyListeners(instance === null || instance === void 0 ? void 0 : instance.vnode.props);
        /**
         * Determines if the prop is v-modeled. Credit to:
         * {@link https://github.com/LinusBorg | Thorsten Lünborg}
         * for coming up with this solution.
         */
        const isVModeled = 'modelValue' in ((_a = instance === null || instance === void 0 ? void 0 : instance.vnode.props) !== null && _a !== void 0 ? _a : {});
        /**
         * Determines if the object being passed as a v-model is reactive.
         */
        // const isReactiveVModel = isVModeled && isReactive(props.modelValue)
        /**
         * Define the initial component
         */
        const value = props.modelValue !== undefined
            ? props.modelValue
            : cloneAny(context.attrs.value);
        /**
         * Creates the node's initial props from the context, props, and definition
         * @returns
         */
        function createInitialProps() {
            const initialProps = {
                ...nodeProps(props),
                ...listeners,
            };
            const attrs = except(nodeProps(context.attrs), pseudoProps);
            if (!attrs.key)
                attrs.key = token();
            initialProps.attrs = attrs;
            const propValues = only(nodeProps(context.attrs), pseudoProps);
            for (const propName in propValues) {
                initialProps[camel(propName)] = propValues[propName];
            }
            const classesProps = { props: {} };
            classesToNodeProps(classesProps, props);
            Object.assign(initialProps, classesProps.props);
            if (typeof initialProps.type !== 'string') {
                initialProps.definition = initialProps.type;
                delete initialProps.type;
            }
            return initialProps;
        }
        /**
         * Create the FormKitNode.
         */
        const initialProps = createInitialProps();
        /**
         * The parent node.
         */
        const parent = initialProps.ignore
            ? null
            : props.parent || vue.inject(parentSymbol, null);
        const node = createNode(extend(config || {}, {
            name: props.name || undefined,
            value,
            parent,
            plugins: (config.plugins || []).concat(props.plugins),
            config: props.config,
            props: initialProps,
            index: props.index,
        }, false, true));
        /**
         * If no definition has been assigned at this point — we're out!
         */
        if (!node.props.definition)
            error(600, node);
        /**
         * All props that are bound "late" (after node creation) — are added to a set
         * which is used to watch the context.attrs object.
         */
        const lateBoundProps = vue.ref(new Set(node.props.definition.props || []));
        /**
         * Any additional props added at a "later" time should also be part of the
         * late bound props.
         */
        node.on('added-props', ({ payload: lateProps }) => {
            if (Array.isArray(lateProps))
                lateProps.forEach((newProp) => lateBoundProps.value.add(newProp));
        });
        /**
         * These prop names must be assigned.
         */
        const pseudoPropNames = vue.computed(() => pseudoProps.concat([...lateBoundProps.value]).reduce((names, prop) => {
            if (typeof prop === 'string') {
                names.push(camel(prop));
                names.push(kebab(prop));
            }
            else {
                names.push(prop);
            }
            return names;
        }, []));
        /* Splits Classes object into discrete props for each key */
        vue.watchEffect(() => classesToNodeProps(node, props));
        /**
         * The props object already has properties even if they start as "undefined"
         * so we can loop over them and individual watchEffect to prevent responding
         * inappropriately.
         */
        const passThrough = nodeProps(props);
        for (const prop in passThrough) {
            vue.watch(() => props[prop], () => {
                if (props[prop] !== undefined) {
                    node.props[prop] = props[prop];
                }
            });
        }
        /**
         * Watch "pseudoProp" attributes explicitly.
         */
        const attributeWatchers = new Set();
        const possibleProps = nodeProps(context.attrs);
        vue.watchEffect(() => {
            watchAttributes(only(possibleProps, pseudoPropNames.value));
        });
        /**
         * Defines attributes that should be used as props.
         * @param attrProps - Attributes that should be used as props instead
         */
        function watchAttributes(attrProps) {
            attributeWatchers.forEach((stop) => {
                stop();
                attributeWatchers.delete(stop);
            });
            for (const prop in attrProps) {
                const camelName = camel(prop);
                attributeWatchers.add(vue.watch(() => context.attrs[prop], () => {
                    node.props[camelName] = context.attrs[prop];
                }));
            }
        }
        /**
         * Watch and dynamically set attribute values, those values that are not
         * props and are not pseudoProps
         */
        vue.watchEffect(() => {
            const attrs = except(nodeProps(context.attrs), pseudoPropNames.value);
            // An explicit exception to ensure naked "multiple" attributes appear on the
            // outer wrapper as data-multiple="true"
            if ('multiple' in attrs)
                attrs.multiple = undefine(attrs.multiple);
            node.props.attrs = Object.assign({}, node.props.attrs || {}, attrs);
        });
        /**
         * Add any/all "prop" errors to the store.
         */
        vue.watchEffect(() => {
            const messages = props.errors.map((error) => createMessage({
                key: slugify(error),
                type: 'error',
                value: error,
                meta: { source: 'prop' },
            }));
            node.store.apply(messages, (message) => message.type === 'error' && message.meta.source === 'prop');
        });
        /**
         * Add input errors.
         */
        if (node.type !== 'input') {
            const sourceKey = `${node.name}-prop`;
            vue.watchEffect(() => {
                const keys = Object.keys(props.inputErrors);
                if (!keys.length)
                    node.clearErrors(true, sourceKey);
                const messages = keys.reduce((messages, key) => {
                    let value = props.inputErrors[key];
                    if (typeof value === 'string')
                        value = [value];
                    if (Array.isArray(value)) {
                        messages[key] = value.map((error) => createMessage({
                            key: error,
                            type: 'error',
                            value: error,
                            meta: { source: sourceKey },
                        }));
                    }
                    return messages;
                }, {});
                node.store.apply(messages, (message) => message.type === 'error' && message.meta.source === sourceKey);
            });
        }
        /**
         * Watch the config prop for any changes.
         */
        vue.watchEffect(() => Object.assign(node.config, props.config));
        /**
         * Produce another parent object.
         */
        if (node.type !== 'input') {
            vue.provide(parentSymbol, node);
        }
        let inputTimeout;
        // eslint-disable-next-line @typescript-eslint/ban-types
        const mutex = new WeakSet();
        /**
         * Explicitly watch the input value, and emit changes (lazy)
         */
        node.on('modelUpdated', () => {
            var _a, _b;
            // Emit the values after commit
            context.emit('inputRaw', (_a = node.context) === null || _a === void 0 ? void 0 : _a.value, node);
            clearTimeout(inputTimeout);
            inputTimeout = setTimeout(context.emit, 20, 'input', (_b = node.context) === null || _b === void 0 ? void 0 : _b.value, node);
            if (isVModeled && node.context) {
                const newValue = useRaw(node.context.value);
                if (isObject(newValue) && useRaw(props.modelValue) !== newValue) {
                    // If this is an object that has been mutated inside FormKit core then
                    // we know when it is emitted it will "return" in the watchVerbose so
                    // we pro-actively add it to the mutex.
                    mutex.add(newValue);
                }
                context.emit('update:modelValue', newValue);
            }
        });
        /**
         * Enabled support for v-model, using this for groups/lists is not recommended
         */
        if (isVModeled) {
            watchVerbose(vue.toRef(props, 'modelValue'), (path, value) => {
                var _a;
                const rawValue = useRaw(value);
                if (isObject(rawValue) && mutex.has(rawValue)) {
                    return mutex.delete(rawValue);
                }
                if (!path.length)
                    node.input(value, false);
                else
                    (_a = node.at(path)) === null || _a === void 0 ? void 0 : _a.input(value, false);
            });
            /**
             * On initialization, if the node’s value was updated (like in a plugin
             * hook) then we should emit a `modelUpdated` event.
             */
            if (node.value !== value) {
                node.emit('modelUpdated');
            }
        }
        /**
         * When this input shuts down, we need to "delete" the node too.
         */
        // onUnmounted(() => node.destroy())
        vue.onBeforeUnmount(() => node.destroy());
        return node;
    }

    /**
     * Creates a plugin based on a list of {@link @formkit/core#FormKitLibrary | FormKitLibrary}.
     *
     * @param libraries - One or many {@link @formkit/core#FormKitLibrary | FormKitLibrary}.
     *
     * @returns {@link @formkit/core#FormKitPlugin | FormKitPlugin}
     *
     * @public
     */
    function createLibraryPlugin(...libraries) {
        /**
         * Merge all provided library items.
         */
        const library = libraries.reduce((merged, lib) => extend(merged, lib), {});
        /* eslint-disable-next-line @typescript-eslint/no-empty-function */
        const plugin = () => { };
        /**
         * Enables the hook that exposes all library inputs.
         * @param node - The primary plugin
         */
        plugin.library = function (node) {
            const type = camel(node.props.type);
            if (has(library, type)) {
                node.define(library[type]);
            }
        };
        return plugin;
    }

    /**
     * A function to normalize an array of objects, array of strings, or object of
     * key-values to use an array of objects with value and label properties.
     *
     * @param options - An un-normalized {@link FormKitOptionsProp | FormKitOptionsProp}.
     *
     * @returns A list of {@link FormKitOptionsList | FormKitOptionsList}.
     *
     * @public
     */
    function normalizeOptions(options) {
        let i = 1;
        if (Array.isArray(options)) {
            return options.map((option) => {
                if (typeof option === 'string' || typeof option === 'number') {
                    return {
                        label: String(option),
                        value: String(option),
                    };
                }
                if (typeof option == 'object') {
                    if ('value' in option && typeof option.value !== 'string') {
                        Object.assign(option, {
                            value: `__mask_${i++}`,
                            __original: option.value,
                        });
                    }
                }
                return option;
            });
        }
        return Object.keys(options).map((value) => {
            return {
                label: options[value],
                value,
            };
        });
    }
    /**
     * Given an {@link FormKitOptionsList | FormKitOptionsList}, find the real value in the options.
     *
     * @param options - The {@link FormKitOptionsList | FormKitOptionsList} to check for a given value
     * @param value - The value to return
     *
     * @returns `unknown`
     *
     * @public
     */
    function optionValue(options, value) {
        if (Array.isArray(options)) {
            for (const option of options) {
                if (value == option.value) {
                    return '__original' in option ? option.__original : option.value;
                }
            }
        }
        return value;
    }
    /**
     * Determines if the value should be selected.
     *
     * @param valueA - Any type of value
     * @param valueB - Any type of value
     *
     * @returns `boolean`
     *
     * @public
     */
    function shouldSelect(valueA, valueB) {
        if (valueA == valueB)
            return true;
        if (isPojo(valueA) && isPojo(valueB))
            return eq(valueA, valueB);
        return false;
    }
    /**
     * A feature that converts the options prop to usable values, to be used by a
     * feature or a plugin.
     *
     * @param node - A {@link @formkit/core#FormKitNode | FormKitNode}.
     *
     * @public
     */
    function options(node) {
        node.hook.prop((prop, next) => {
            if (prop.prop === 'options') {
                if (typeof prop.value === 'function') {
                    node.props.optionsLoader = prop.value;
                    prop.value = [];
                }
                else {
                    prop.value = normalizeOptions(prop.value);
                }
            }
            return next(prop);
        });
    }

    /**
     * Outer section where most data attributes are assigned.
     *
     * @public
     */
    const outer = createSection('outer', () => ({
        $el: 'div',
        attrs: {
            key: '$id',
            'data-family': '$family || undefined',
            'data-type': '$type',
            'data-multiple': '$attrs.multiple || ($type != "select" && $options != undefined) || undefined',
            'data-disabled': '$disabled || undefined',
            'data-complete': '$state.complete || undefined',
            'data-invalid': '$state.valid === false && $state.validationVisible || undefined',
            'data-errors': '$state.errors || undefined',
            'data-submitted': '$state.submitted || undefined',
            'data-prefix-icon': '$_rawPrefixIcon !== undefined || undefined',
            'data-suffix-icon': '$_rawSuffixIcon !== undefined || undefined',
            'data-prefix-icon-click': '$onPrefixIconClick !== undefined || undefined',
            'data-suffix-icon-click': '$onSuffixIconClick !== undefined || undefined',
        },
    }), true);

    /**
     * Inner section
     *
     * @public
     */
    const inner = createSection('inner', 'div');

    /**
     * Wrapper input section
     *
     * @public
     */
    const wrapper = createSection('wrapper', 'div');

    /**
     * Label section with label element
     *
     * @public
     */
    const label = createSection('label', () => ({
        $el: 'label',
        if: '$label',
        attrs: {
            for: '$id',
        },
    }));

    /**
     * Messages section where all messages will be displayed.
     *
     * @public
     */
    const messages$1 = createSection('messages', () => ({
        $el: 'ul',
        if: '$defaultMessagePlacement && $fns.length($messages)',
    }));

    /**
     * Message section, shows a group of messages.
     *
     * @public
     */
    const message$1 = createSection('message', () => ({
        $el: 'li',
        for: ['message', '$messages'],
        attrs: {
            key: '$message.key',
            id: `$id + '-' + $message.key`,
            'data-message-type': '$message.type',
        },
    }));

    /**
     * Prefix section
     *
     * @public
     */
    const prefix = createSection('prefix', null);

    /**
     * Suffix section
     *
     * @public
     */
    const suffix = createSection('suffix', null);

    /**
     * Help section that shows help text
     *
     * @public
     */
    const help = createSection('help', () => ({
        $el: 'div',
        if: '$help',
        attrs: {
            id: '$: "help-" + $id',
        },
    }));

    /**
     * Fieldset section, used to describe help
     *
     * @public
     */
    const fieldset = createSection('fieldset', () => ({
        $el: 'fieldset',
        attrs: {
            id: '$id',
            'aria-describedby': {
                if: '$help',
                then: '$: "help-" + $id',
                else: undefined,
            },
        },
    }));

    /**
     * Decorator section
     *
     * @public
     */
    const decorator = createSection('decorator', () => ({
        $el: 'span',
        attrs: {
            'aria-hidden': 'true',
        },
    }));

    /**
     * Box section used for grouping options
     *
     * @public
     */
    const box = createSection('input', () => ({
        $el: 'input',
        bind: '$attrs',
        attrs: {
            type: '$type',
            name: '$node.props.altName || $node.name',
            disabled: '$option.attrs.disabled || $disabled',
            onInput: '$handlers.toggleChecked',
            checked: '$fns.eq($_value, $onValue)',
            onBlur: '$handlers.blur',
            value: '$: true',
            id: '$id',
            'aria-describedby': {
                if: '$options.length',
                then: {
                    if: '$option.help',
                    then: '$: "help-" + $option.attrs.id',
                    else: undefined,
                },
                else: {
                    if: '$help',
                    then: '$: "help-" + $id',
                    else: undefined,
                },
            },
        },
    }));

    /**
     * Legend section, used instead of label when its grouping fields.
     *
     * @public
     */
    const legend = createSection('legend', () => ({
        $el: 'legend',
        if: '$label',
    }));

    /**
     * Option section used to show an option
     *
     * @public
     */
    const boxOption = createSection('option', () => ({
        $el: 'li',
        for: ['option', '$options'],
        attrs: {
            'data-disabled': '$option.attrs.disabled || $disabled',
        },
    }));

    /**
     * Options section used to wrap all option sections in a list
     *
     * @public
     */
    const boxOptions = createSection('options', 'ul');

    /**
     * Wrapper section for options
     *
     * @public
     */
    const boxWrapper = createSection('wrapper', () => ({
        $el: 'label',
        attrs: {
            'data-disabled': {
                if: '$options.length',
                then: undefined,
                else: '$disabled || undefined',
            },
            'data-checked': {
                if: '$options == undefined',
                then: '$fns.eq($_value, $onValue) || undefined',
                else: '$fns.isChecked($option.value) || undefined',
            },
        },
    }));

    /**
     * Option help section
     *
     * @public
     */
    const boxHelp = createSection('optionHelp', () => ({
        $el: 'div',
        if: '$option.help',
        attrs: {
            id: '$: "help-" + $option.attrs.id',
        },
    }));

    /**
     * Label section for options
     *
     * @public
     */
    const boxLabel = createSection('label', 'span');

    /**
     * Input section for a button
     *
     * @public
     */
    const buttonInput = createSection('input', () => ({
        $el: 'button',
        bind: '$attrs',
        attrs: {
            type: '$type',
            disabled: '$disabled',
            name: '$node.name',
            id: '$id',
        },
    }));

    /**
     * Default section for a button
     *
     * @public
     */
    const buttonLabel = createSection('default', null);

    /**
     * Input section for a file input
     *
     * @public
     */
    const fileInput = createSection('input', () => ({
        $el: 'input',
        bind: '$attrs',
        attrs: {
            type: 'file',
            disabled: '$disabled',
            name: '$node.name',
            onChange: '$handlers.files',
            onBlur: '$handlers.blur',
            id: '$id',
            'aria-describedby': '$describedBy',
        },
    }));

    /**
     * File item section for showing a file name
     *
     * @public
     */
    const fileItem = createSection('fileItem', () => ({
        $el: 'li',
        for: ['file', '$value'],
    }));

    /**
     * File list section to show all file names
     *
     * @public
     */
    const fileList = createSection('fileList', () => ({
        $el: 'ul',
        if: '$value.length',
        attrs: {
            'data-has-multiple': {
                if: '$value.length > 1',
                then: 'true',
            },
        },
    }));

    /**
     * File name section to show the file name
     *
     * @public
     */
    const fileName = createSection('fileName', () => ({
        $el: 'span',
        attrs: {
            class: '$classes.fileName',
        },
    }));

    /**
     * File remove section to show a remove button for files
     *
     * @public
     */
    const fileRemove = createSection('fileRemove', () => ({
        $el: 'button',
        attrs: {
            onClick: '$handlers.resetFiles',
        },
    }));

    /**
     * No file section that shows when there is no files
     *
     * @public
     */
    const noFiles = createSection('noFiles', () => ({
        $el: 'span',
        if: '$value.length == 0',
    }));

    /**
     * Form section
     *
     * @public
     */
    const formInput = createSection('form', () => ({
        $el: 'form',
        bind: '$attrs',
        attrs: {
            id: '$id',
            name: '$node.name',
            onSubmit: '$handlers.submit',
            'data-loading': '$state.loading || undefined',
        },
    }), true);

    /**
     * Actions section that shows the action buttons
     *
     * @public
     */
    const actions = createSection('actions', () => ({
        $el: 'div',
        if: '$actions',
    }));

    /**
     * Submit section that displays a submit button from a form
     *
     * @public
     */
    const submitInput = createSection('submit', () => ({
        $cmp: 'FormKit',
        bind: '$submitAttrs',
        props: {
            type: 'submit',
            disabled: '$disabled',
            label: '$submitLabel',
        },
    }));

    /**
     * Input section
     *
     * @public
     */
    const textInput = createSection('input', () => ({
        $el: 'input',
        bind: '$attrs',
        attrs: {
            type: '$type',
            disabled: '$disabled',
            name: '$node.name',
            onInput: '$handlers.DOMInput',
            onBlur: '$handlers.blur',
            value: '$_value',
            id: '$id',
            'aria-describedby': '$describedBy',
        },
    }));

    /**
     * A simple fragment section
     *
     * @public
     */
    const fragment = createSection('wrapper', null, true);

    /**
     * Input section used by selects
     *
     * @public
     */
    const selectInput$1 = createSection('input', () => ({
        $el: 'select',
        bind: '$attrs',
        attrs: {
            id: '$id',
            'data-placeholder': '$fns.showPlaceholder($_value, $placeholder)',
            disabled: '$disabled',
            class: '$classes.input',
            name: '$node.name',
            onChange: '$handlers.onChange',
            onInput: '$handlers.selectInput',
            onBlur: '$handlers.blur',
            'aria-describedby': '$describedBy',
        },
    }));

    /**
     * Option section used to show options
     *
     * @public
     */
    const option = createSection('option', () => ({
        $el: 'option',
        for: ['option', '$options'],
        bind: '$option.attrs',
        attrs: {
            class: '$classes.option',
            value: '$option.value',
            selected: '$fns.isSelected($option)',
        },
    }));

    /**
     * Options slot section that displays options when used with slots
     *
     * @public
     */
    const optionSlot = () => ({
        $el: null,
        if: '$options.length',
        for: ['option', '$options'],
        children: '$slots.option',
    });

    /**
     * Input section used by textarea inputs
     *
     * @public
     */
    const textareaInput = createSection('input', () => ({
        $el: 'textarea',
        bind: '$attrs',
        attrs: {
            disabled: '$disabled',
            name: '$node.name',
            onInput: '$handlers.DOMInput',
            onBlur: '$handlers.blur',
            value: '$_value',
            id: '$id',
            'aria-describedby': '$describedBy',
        },
        children: '$initialValue',
    }));

    /**
     * Icon section used by all icons
     *
     * @public
     */
    const icon = (sectionKey, el) => {
        return createSection(`${sectionKey}Icon`, () => {
            const rawIconProp = `_raw${sectionKey
            .charAt(0)
            .toUpperCase()}${sectionKey.slice(1)}Icon`;
            return {
                if: `$${sectionKey}Icon && $${rawIconProp}`,
                $el: `${el ? el : 'span'}`,
                attrs: {
                    class: `$classes.${sectionKey}Icon + " formkit-icon"`,
                    innerHTML: `$${rawIconProp}`,
                    onClick: `$handlers.iconClick(${sectionKey})`,
                    for: {
                        if: `${el === 'label'}`,
                        then: '$id',
                    },
                },
            };
        })();
    };

    /**
     * A feature that normalizes box types (checkboxes, radios).
     *
     * @param node - A {@link @formkit/core#FormKitNode | FormKitNode}.
     *
     * @returns A {@link @formkit/node#FormKitMiddleware | FormKitMiddleware}.
     *
     * @public
     */
    function normalizeBoxes(node) {
        return function (prop, next) {
            if (prop.prop === 'options' && Array.isArray(prop.value)) {
                prop.value = prop.value.map((option) => {
                    var _a;
                    if (!((_a = option.attrs) === null || _a === void 0 ? void 0 : _a.id)) {
                        return extend(option, {
                            attrs: { id: `${node.name}-option-${slugify(String(option.value))}` },
                        });
                    }
                    return option;
                });
                if (node.props.type === 'checkbox' && !Array.isArray(node.value)) {
                    if (node.isCreated) {
                        node.input([], false);
                    }
                    else {
                        node.on('created', () => {
                            if (!Array.isArray(node.value)) {
                                node.input([], false);
                            }
                        });
                    }
                }
            }
            return next(prop);
        };
    }

    /**
     * Event handler when an input is toggled.
     *
     * @param node - The {@link @formkit/node#FormKitNode | FormKitNode} being toggled.
     * @param e - The input event related to the toggling.
     *
     * @public
     */
    function toggleChecked$1(node, e) {
        const el = e.target;
        if (el instanceof HTMLInputElement) {
            const value = Array.isArray(node.props.options)
                ? optionValue(node.props.options, el.value)
                : el.value;
            if (Array.isArray(node.props.options) && node.props.options.length) {
                if (!Array.isArray(node._value)) {
                    // There is no array value set
                    node.input([value]);
                }
                else if (!node._value.some((existingValue) => shouldSelect(value, existingValue))) {
                    // The value is not in the current set
                    node.input([...node._value, value]);
                }
                else {
                    // Filter out equivalent values
                    node.input(node._value.filter((existingValue) => !shouldSelect(value, existingValue)));
                }
            }
            else {
                if (el.checked) {
                    node.input(node.props.onValue);
                }
                else {
                    node.input(node.props.offValue);
                }
            }
        }
    }
    /**
     * Checks if a given option is present in the node value.
     *
     * @param node - The The {@link @formkit/node#FormKitNode | FormKitNode} being checked.
     * @param value - The value of any option.
     *
     * @returns `boolean`
     *
     * @public
     */
    function isChecked$1(node, value) {
        var _a, _b;
        // We need to force vue’s reactivity to respond when the value is run:
        (_a = node.context) === null || _a === void 0 ? void 0 : _a.value;
        (_b = node.context) === null || _b === void 0 ? void 0 : _b._value;
        if (Array.isArray(node._value)) {
            return node._value.some((existingValue) => shouldSelect(optionValue(node.props.options, value), existingValue));
        }
        return false;
    }
    /**
     * A feature that adds checkbox selection support.
     *
     * @param node - A {@link @formkit/core#FormKitNode | FormKitNode}.
     *
     * @public
     */
    function checkboxes(node) {
        node.on('created', () => {
            var _a, _b;
            if ((_a = node.context) === null || _a === void 0 ? void 0 : _a.handlers) {
                node.context.handlers.toggleChecked = toggleChecked$1.bind(null, node);
            }
            if ((_b = node.context) === null || _b === void 0 ? void 0 : _b.fns) {
                node.context.fns.isChecked = isChecked$1.bind(null, node);
            }
            // Configure our default onValue and offValue
            if (!has(node.props, 'onValue'))
                node.props.onValue = true;
            if (!has(node.props, 'offValue'))
                node.props.offValue = false;
        });
        node.hook.prop(normalizeBoxes(node));
    }

    /**
     * A feature that allows disabling children of this node.
     *
     * @param node - A {@link @formkit/core#FormKitNode | FormKitNode}.
     *
     * @public
     */
    function disables(node) {
        node.on('created', () => {
            node.props.disabled = undefine(node.props.disabled);
        });
        node.hook.prop(({ prop, value }, next) => {
            value = prop === 'disabled' ? undefine(value) : value;
            return next({ prop, value });
        });
        node.on('prop:disabled', ({ payload: value }) => {
            node.config.disabled = undefine(value);
        });
        node.on('created', () => {
            node.config.disabled = undefine(node.props.disabled);
        });
    }

    /**
     * Creates a new feature that generates a localization message of type ui
     * for use on a given component.
     *
     * @param key - The key of the message.
     * @param value - The value of the message.
     *
     * @returns A {@link @formkit/core#FormKitPlugin | FormKitPlugin}.
     *
     * @public
     */
    function localize(key, value) {
        return (node) => {
            node.store.set(createMessage({
                key,
                type: 'ui',
                value: value || key,
                meta: {
                    localize: true,
                    i18nArgs: [node],
                },
            }));
        };
    }

    const isBrowser = typeof window !== 'undefined';
    /**
     * Remove the data-file-hover attribute from the target.
     *
     * @param e - Event
     *
     * @internal
     */
    function removeHover(e) {
        if (e.target instanceof HTMLElement &&
            e.target.hasAttribute('data-file-hover')) {
            e.target.removeAttribute('data-file-hover');
        }
    }
    /**
     * Prevent stray drag/drop events from navigating the window.
     *
     * @param e - Event
     *
     * @internal
     */
    function preventStrayDrop(type, e) {
        if (!(e.target instanceof HTMLInputElement)) {
            e.preventDefault();
        }
        else if (type === 'dragover') {
            e.target.setAttribute('data-file-hover', 'true');
        }
        if (type === 'drop') {
            removeHover(e);
        }
    }
    /**
     * A feature to add file handling support to an input.
     *
     * @param node - A {@link @formkit/core#FormKitNode | FormKitNode}.
     *
     * @public
     */
    function files(node) {
        // Localize our content:
        localize('noFiles', 'Select file')(node);
        localize('removeAll', 'Remove all')(node);
        localize('remove')(node);
        if (isBrowser) {
            if (!window._FormKit_File_Drop) {
                window.addEventListener('dragover', preventStrayDrop.bind(null, 'dragover'));
                window.addEventListener('drop', preventStrayDrop.bind(null, 'drop'));
                window.addEventListener('dragleave', removeHover);
                window._FormKit_File_Drop = true;
            }
        }
        node.hook.input((value, next) => next(Array.isArray(value) ? value : []));
        node.on('created', () => {
            if (!Array.isArray(node.value))
                node.input([], false);
            if (!node.context)
                return;
            node.context.handlers.resetFiles = (e) => {
                e.preventDefault();
                node.input([]);
                if (node.props.id && isBrowser) {
                    const el = document.getElementById(node.props.id);
                    if (el)
                        el.value = '';
                }
            };
            node.context.handlers.files = (e) => {
                var _a, _b;
                const files = [];
                if (e.target instanceof HTMLInputElement && e.target.files) {
                    for (let i = 0; i < e.target.files.length; i++) {
                        let file;
                        if ((file = e.target.files.item(i))) {
                            files.push({ name: file.name, file });
                        }
                    }
                    node.input(files);
                }
                if (node.context)
                    node.context.files = files;
                // Call the original listener if there is one.
                if (typeof ((_a = node.props.attrs) === null || _a === void 0 ? void 0 : _a.onChange) === 'function') {
                    (_b = node.props.attrs) === null || _b === void 0 ? void 0 : _b.onChange(e);
                }
            };
        });
    }

    /**
     * Handle the submit event.
     *
     * @param e - The event
     *
     * @internal
     */
    async function handleSubmit(node, submitEvent) {
        submitEvent.preventDefault();
        await node.settled;
        // Set the submitted state on all children
        node.walk((n) => {
            n.store.set(createMessage({
                key: 'submitted',
                value: true,
                visible: false,
            }));
        });
        if (typeof node.props.onSubmitRaw === 'function') {
            node.props.onSubmitRaw(submitEvent, node);
        }
        if (node.ledger.value('blocking')) {
            if (typeof node.props.onSubmitInvalid === 'function') {
                node.props.onSubmitInvalid(node);
            }
            // There is still a blocking message in the store.
            if (node.props.incompleteMessage !== false) {
                node.store.set(createMessage({
                    blocking: false,
                    key: `incomplete`,
                    meta: {
                        localize: node.props.incompleteMessage === undefined,
                        i18nArgs: [{ node }],
                        showAsMessage: true,
                    },
                    type: 'ui',
                    value: node.props.incompleteMessage || 'Form incomplete.',
                }));
            }
        }
        else {
            // No blocking messages
            if (typeof node.props.onSubmit === 'function') {
                // call onSubmit
                const retVal = node.props.onSubmit(node.hook.submit.dispatch(clone(node.value)), node);
                if (retVal instanceof Promise) {
                    const autoDisable = node.props.disabled === undefined &&
                        node.props.submitBehavior !== 'live';
                    if (autoDisable)
                        node.props.disabled = true;
                    node.store.set(createMessage({
                        key: 'loading',
                        value: true,
                        visible: false,
                    }));
                    await retVal;
                    if (autoDisable)
                        node.props.disabled = false;
                    node.store.remove('loading');
                }
            }
            else {
                if (submitEvent.target instanceof HTMLFormElement) {
                    submitEvent.target.submit();
                }
            }
        }
    }
    /**
     * A feature to add a submit handler and actions section.
     *
     * @param node - A {@link @formkit/core#FormKitNode | FormKitNode}.
     *
     * @public
     */
    function form$1(node) {
        node.props.isForm = true;
        node.on('created', () => {
            var _a;
            if ((_a = node.context) === null || _a === void 0 ? void 0 : _a.handlers) {
                node.context.handlers.submit = handleSubmit.bind(null, node);
            }
            if (!has(node.props, 'actions')) {
                node.props.actions = true;
            }
        });
        node.on('settled:blocking', () => node.store.remove('incomplete'));
    }

    /**
     * A feature that applies `ignore="true"` by default.
     *
     * @param node - A {@link @formkit/core#FormKitNode | FormKitNode}.
     *
     * @public
     */
    function ignore(node) {
        if (node.props.ignore === undefined) {
            node.props.ignore = true;
            node.parent = null;
        }
    }

    /**
     * A feature that ensures the input has an `initialValue` prop.
     *
     * @param node - A {@link @formkit/core#FormKitNode | FormKitNode}.
     *
     * @public
     */
    function initialValue(node) {
        node.on('created', () => {
            if (node.context) {
                node.context.initialValue = node.value || '';
            }
        });
    }

    /**
     * Sets the value of a radio button when checked.
     *
     * @param node - A {@link @formkit/core#FormKitNode | FormKitNode}.
     * @param event - Event
     *
     * @public
     */
    function toggleChecked(node, event) {
        if (event.target instanceof HTMLInputElement) {
            node.input(optionValue(node.props.options, event.target.value));
        }
    }
    /**
     * Checks if the value being checked is the current value.
     *
     * @param node - A {@link @formkit/core#FormKitNode | FormKitNode}.
     * @param value - The value to check
     *
     * @returns `boolean`
     *
     * @public
     */
    function isChecked(node, value) {
        var _a, _b;
        // We need to force vue’s reactivity to respond when the value is run:
        (_a = node.context) === null || _a === void 0 ? void 0 : _a.value;
        (_b = node.context) === null || _b === void 0 ? void 0 : _b._value;
        return shouldSelect(optionValue(node.props.options, value), node._value);
    }
    /**
     * A feature to check if the value being checked is the current value.
     *
     * @param node - A {@link @formkit/core#FormKitNode | FormKitNode}.
     *
     * @public
     */
    function radios(node) {
        node.on('created', () => {
            var _a, _b;
            if (!Array.isArray(node.props.options)) {
                warn(350, node);
            }
            if ((_a = node.context) === null || _a === void 0 ? void 0 : _a.handlers) {
                node.context.handlers.toggleChecked = toggleChecked.bind(null, node);
            }
            if ((_b = node.context) === null || _b === void 0 ? void 0 : _b.fns) {
                node.context.fns.isChecked = isChecked.bind(null, node);
            }
        });
        node.hook.prop(normalizeBoxes(node));
    }

    /**
     * Checks if a the given option should have the selected attribute.
     * @param node - The node being evaluated.
     * @param option - The option value to check
     * @returns
     * @public
     */
    function isSelected(node, option) {
        // Here we trick reactivity (if at play) to watch this function.
        node.context && node.context.value;
        const optionValue = '__original' in option ? option.__original : option.value;
        function hasNoNullOption() {
            return !node.props.options.some((option) => ('__original' in option ? option.__original : option.value) === null);
        }
        return Array.isArray(node._value)
            ? node._value.some((optionA) => shouldSelect(optionA, optionValue))
            : (node._value === undefined ||
                (node._value === null && hasNoNullOption())) &&
                option.attrs &&
                option.attrs['data-is-placeholder']
                ? true
                : shouldSelect(optionValue, node._value);
    }
    /**
     * Defers the change event till after the next cycle.
     * @param node - The node being evaluated.
     * @param e - The change event.
     */
    async function deferChange(node, e) {
        var _a;
        if (typeof ((_a = node.props.attrs) === null || _a === void 0 ? void 0 : _a.onChange) === 'function') {
            await new Promise((r) => setTimeout(r, 0));
            await node.settled;
            node.props.attrs.onChange(e);
        }
    }
    /**
     * Select the correct values.
     * @param e - The input event emitted by the select.
     */
    function selectInput(node, e) {
        const target = e.target;
        const value = target.hasAttribute('multiple')
            ? Array.from(target.selectedOptions).map((o) => optionValue(node.props.options, o.value))
            : optionValue(node.props.options, target.value);
        node.input(value);
    }
    /**
     * Appends a placeholder to the options list.
     * @param options - An options list
     * @param placeholder - A placeholder string to append
     * @returns
     */
    function applyPlaceholder(options, placeholder) {
        if (!options.some((option) => option.attrs && option.attrs['data-is-placeholder'])) {
            return [
                {
                    label: placeholder,
                    value: '',
                    attrs: {
                        hidden: true,
                        disabled: true,
                        'data-is-placeholder': 'true',
                    },
                },
                ...options,
            ];
        }
        return options;
    }
    /**
     * Converts the options prop to usable values.
     * @param node - A formkit node.
     * @public
     */
    function select$1(node) {
        // Set the initial value of a multi-input
        node.on('created', () => {
            var _a, _b, _c;
            const isMultiple = undefine((_a = node.props.attrs) === null || _a === void 0 ? void 0 : _a.multiple);
            if (!isMultiple &&
                node.props.placeholder &&
                Array.isArray(node.props.options)) {
                node.hook.prop(({ prop, value }, next) => {
                    if (prop === 'options') {
                        value = applyPlaceholder(value, node.props.placeholder);
                    }
                    return next({ prop, value });
                });
                node.props.options = applyPlaceholder(node.props.options, node.props.placeholder);
            }
            if (isMultiple) {
                if (node.value === undefined) {
                    node.input([], false);
                }
            }
            else if (node.context && !node.context.options) {
                // If this input is (probably) using the default slot, we need to add a
                // "value" attribute to get bound
                node.props.attrs = Object.assign({}, node.props.attrs, {
                    value: node._value,
                });
                node.on('input', ({ payload }) => {
                    node.props.attrs = Object.assign({}, node.props.attrs, {
                        value: payload,
                    });
                });
            }
            if ((_b = node.context) === null || _b === void 0 ? void 0 : _b.handlers) {
                node.context.handlers.selectInput = selectInput.bind(null, node);
                node.context.handlers.onChange = deferChange.bind(null, node);
            }
            if ((_c = node.context) === null || _c === void 0 ? void 0 : _c.fns) {
                node.context.fns.isSelected = isSelected.bind(null, node);
                node.context.fns.showPlaceholder = (value, placeholder) => {
                    if (!Array.isArray(node.props.options))
                        return false;
                    const hasMatchingValue = node.props.options.some((option) => {
                        if (option.attrs && 'data-is-placeholder' in option.attrs)
                            return false;
                        const optionValue = '__original' in option ? option.__original : option.value;
                        return eq(value, optionValue);
                    });
                    return placeholder && !hasMatchingValue ? true : undefined;
                };
            }
        });
        node.hook.input((value, next) => {
            var _a, _b, _c;
            if (!node.props.placeholder &&
                value === undefined &&
                Array.isArray((_a = node.props) === null || _a === void 0 ? void 0 : _a.options) &&
                node.props.options.length &&
                !undefine((_c = (_b = node.props) === null || _b === void 0 ? void 0 : _b.attrs) === null || _c === void 0 ? void 0 : _c.multiple)) {
                value =
                    '__original' in node.props.options[0]
                        ? node.props.options[0].__original
                        : node.props.options[0].value;
            }
            return next(value);
        });
    }

    /**
     * Adds icon props definition.
     *
     * @param sectionKey - the location the icon should be loaded.
     * @param defaultIcon - the icon that should be loaded if a match is found in the user's CSS.
     *
     * @returns A {@link @formkit/core#FormKitPlugin | FormKitPlugin}.
     *
     * @public
     */
    function defaultIcon(sectionKey, defaultIcon) {
        return (node) => {
            if (node.props[`${sectionKey}Icon`] === undefined) {
                node.props[`${sectionKey}Icon`] = `default:${defaultIcon}`;
            }
        };
    }

    /**
     * Type guard for schema objects.
     *
     * @param schema - returns `true` if the node is a schema node but not a string
     * or conditional.
     *
     * @returns `boolean`
     *
     * @public
     */
    function isSchemaObject(schema) {
        return (typeof schema === 'object' &&
            ('$el' in schema || '$cmp' in schema || '$formkit' in schema));
    }
    /**
     * Checks if the current schema node is a slot condition.
     *
     * @example
     *
     * ```js
     * {
     *  if: '$slot.name',
     *  then: '$slot.name',
     *  else: []
     * } // this schema node would return true.
     * ```
     *
     * @param node - A {@link @formkit/core#FormKitSchemaNode | FormKitSchemaNode}.
     *
     * @returns `boolean`
     *
     * @public
     */
    function isSlotCondition(node) {
        if (isConditional(node) &&
            node.if &&
            node.if.startsWith('$slots.') &&
            typeof node.then === 'string' &&
            node.then.startsWith('$slots.') &&
            'else' in node) {
            return true;
        }
        return false;
    }
    /**
     * Extends a single schema node with an extension. The extension can be any
     * partial node including strings.
     *
     * @param schema - The base schema node.
     * @param extension - The values to extend on the base schema node.
     *
     * @returns {@link @formkit/core#FormKitSchemaNode | FormKitSchemaNode}
     *
     * @public
     */
    function extendSchema(schema, extension = {}) {
        if (typeof schema === 'string') {
            return isSchemaObject(extension) || typeof extension === 'string'
                ? extension
                : schema;
        }
        else if (Array.isArray(schema)) {
            return isSchemaObject(extension) ? extension : schema;
        }
        return extend(schema, extension);
    }
    /**
     * Creates an input schema with all of the wrapping base schema.
     *
     * @param inputSection - Content to store in the input section key location.
     *
     * @returns {@link @formkit/core#FormKitExtendableSchemaRoot | FormKitExtendableSchemaRoot}
     *
     * @public
     */
    function useSchema(inputSection) {
        return outer(wrapper(label('$label'), inner(prefix(), inputSection(), suffix())), help('$help'), messages$1(message$1('$message.value')));
    }
    function createSection(section, el, root = false) {
        return (...children) => {
            const extendable = (extensions) => {
                const node = !el || typeof el === 'string' ? { $el: el } : el();
                if (isDOM(node) || isComponent$1(node)) {
                    if (!node.meta) {
                        node.meta = { section };
                    }
                    if (children.length && !node.children) {
                        node.children = [
                            ...children.map((child) => typeof child === 'string' ? child : child(extensions)),
                        ];
                    }
                    if (isDOM(node)) {
                        node.attrs = {
                            class: `$classes.${section}`,
                            ...(node.attrs || {}),
                        };
                    }
                }
                return {
                    if: `$slots.${section}`,
                    then: `$slots.${section}`,
                    else: section in extensions
                        ? extendSchema(node, extensions[section])
                        : node,
                };
            };
            extendable._s = section;
            return root ? createRoot(extendable) : extendable;
        };
    }
    /**
     * Returns an extendable schema root node.
     *
     * @param rootSection - Creates the root node.
     *
     * @returns {@link @formkit/core#FormKitExtendableSchemaRoot | FormKitExtendableSchemaRoot}
     *
     * @internal
     */
    function createRoot(rootSection) {
        return (extensions) => {
            return [rootSection(extensions)];
        };
    }
    /**
     * Applies a condition to a given schema section.
     *
     * @param condition - A schema condition to apply to a section.
     * @param then - The section that applies if the condition is true.
     * @param otherwise - (else) The section that applies if the condition is false.
     *
     * @returns {@link FormKitSchemaExtendableSection | FormKitSchemaExtendableSection}
     *
     * @public
     */
    function $if(condition, then, otherwise) {
        const extendable = (extensions) => {
            const node = then(extensions);
            if (otherwise ||
                (isSchemaObject(node) && 'if' in node) ||
                isSlotCondition(node)) {
                const conditionalNode = {
                    if: condition,
                    then: node,
                };
                if (otherwise) {
                    conditionalNode.else = otherwise(extensions);
                }
                return conditionalNode;
            }
            else if (isSlotCondition(node)) {
                Object.assign(node.else, { if: condition });
            }
            else if (isSchemaObject(node)) {
                Object.assign(node, { if: condition });
            }
            return node;
        };
        extendable._s = token();
        return extendable;
    }
    /**
     * Extends a schema node with a given set of extensions.
     *
     * @param section - A section to apply an extension to.
     * @param extendWith - A partial schema snippet to apply to the section.
     *
     * @returns {@link FormKitSchemaExtendableSection | FormKitSchemaExtendableSection}
     *
     * @public
     */
    function $extend(section, extendWith) {
        const extendable = (extensions) => {
            const node = section({});
            if (isSlotCondition(node)) {
                if (Array.isArray(node.else))
                    return node;
                node.else = extendSchema(extendSchema(node.else, extendWith), section._s ? extensions[section._s] : {});
                return node;
            }
            return extendSchema(extendSchema(node, extendWith), section._s ? extensions[section._s] : {});
        };
        extendable._s = section._s;
        return extendable;
    }
    /**
     * Creates a root schema section.
     *
     * @param section - A section to make a root from.
     *
     * @returns {@link FormKitSchemaExtendableSection | FormKitSchemaExtendableSection}
     *
     * @public
     */
    function $root(section) {
        return createRoot(section);
    }

    /**
     * Input definition for a button.
     * @public
     */
    const button = {
        /**
         * The actual schema of the input, or a function that returns the schema.
         */
        schema: outer(messages$1(message$1('$message.value')), wrapper(buttonInput(icon('prefix'), prefix(), buttonLabel('$label || $ui.submit.value'), suffix(), icon('suffix'))), help('$help')),
        /**
         * The type of node, can be a list, group, or input.
         */
        type: 'input',
        /**
         * The family of inputs this one belongs too. For example "text" and "email"
         * are both part of the "text" family. This is primary used for styling.
         */
        family: 'button',
        /**
         * An array of extra props to accept for this input.
         */
        props: [],
        /**
         * Additional features that should be added to your input
         */
        features: [localize('submit'), ignore],
    };

    /**
     * Input definition for a checkbox(ess).
     * @public
     */
    const checkbox = {
        /**
         * The actual schema of the input, or a function that returns the schema.
         */
        schema: outer($if('$options == undefined', 
        /**
         * Single checkbox structure.
         */
        boxWrapper(inner(prefix(), box(), decorator(icon('decorator')), suffix()), $extend(boxLabel('$label'), {
            if: '$label',
        })), 
        /**
         * Multi checkbox structure.
         */
        fieldset(legend('$label'), help('$help'), boxOptions(boxOption(boxWrapper(inner(prefix(), $extend(box(), {
            bind: '$option.attrs',
            attrs: {
                id: '$option.attrs.id',
                value: '$option.value',
                checked: '$fns.isChecked($option.value)',
            },
        }), decorator(icon('decorator')), suffix()), $extend(boxLabel('$option.label'), {
            if: '$option.label',
        })), boxHelp('$option.help'))))), 
        // Help text only goes under the input when it is a single.
        $if('$options == undefined && $help', help('$help')), messages$1(message$1('$message.value'))),
        /**
         * The type of node, can be a list, group, or input.
         */
        type: 'input',
        /**
         * The family of inputs this one belongs too. For example "text" and "email"
         * are both part of the "text" family. This is primary used for styling.
         */
        family: 'box',
        /**
         * An array of extra props to accept for this input.
         */
        props: ['options', 'onValue', 'offValue', 'optionsLoader'],
        /**
         * Additional features that should be added to your input
         */
        features: [
            options,
            checkboxes,
            defaultIcon('decorator', 'checkboxDecorator'),
        ],
    };

    /**
     * Input definition for a file input.
     * @public
     */
    const file = {
        /**
         * The actual schema of the input, or a function that returns the schema.
         */
        schema: outer(wrapper(label('$label'), inner(icon('prefix', 'label'), prefix(), fileInput(), fileList(fileItem(icon('fileItem'), fileName('$file.name'), $if('$value.length === 1', fileRemove(icon('fileRemove'), '$ui.remove.value')))), $if('$value.length > 1', fileRemove('$ui.removeAll.value')), noFiles(icon('noFiles'), '$ui.noFiles.value'), suffix(), icon('suffix'))), help('$help'), messages$1(message$1('$message.value'))),
        /**
         * The type of node, can be a list, group, or input.
         */
        type: 'input',
        /**
         * The family of inputs this one belongs too. For example "text" and "email"
         * are both part of the "text" family. This is primary used for styling.
         */
        family: 'text',
        /**
         * An array of extra props to accept for this input.
         */
        props: [],
        /**
         * Additional features that should be added to your input
         */
        features: [
            files,
            defaultIcon('fileItem', 'fileItem'),
            defaultIcon('fileRemove', 'fileRemove'),
            defaultIcon('noFiles', 'noFiles'),
        ],
    };

    /**
     * Input definition for a form.
     * @public
     */
    const form = {
        /**
         * The actual schema of the input, or a function that returns the schema.
         */
        schema: formInput('$slots.default', messages$1(message$1('$message.value')), actions(submitInput())),
        /**
         * The type of node, can be a list, group, or input.
         */
        type: 'group',
        /**
         * An array of extra props to accept for this input.
         */
        props: [
            'actions',
            'submit',
            'submitLabel',
            'submitAttrs',
            'submitBehavior',
            'incompleteMessage',
        ],
        /**
         * Additional features that should be added to your input
         */
        features: [form$1, disables],
    };

    /**
     * Input definition for a group.
     * @public
     */
    const group = {
        /**
         * The actual schema of the input, or a function that returns the schema.
         */
        schema: fragment('$slots.default'),
        /**
         * The type of node, can be a list, group, or input.
         */
        type: 'group',
        /**
         * An array of extra props to accept for this input.
         */
        props: [],
        /**
         * Additional features that should be added to your input
         */
        features: [disables],
    };

    /**
     * Input definition for a hidden input.
     * @public
     */
    const hidden = {
        /**
         * The actual schema of the input, or a function that returns the schema.
         */
        schema: $root(textInput()),
        /**
         * The type of node, can be a list, group, or input.
         */
        type: 'input',
        /**
         * An array of extra props to accept for this input.
         */
        props: [],
        /**
         * Additional features that should be added to your input
         */
        features: [],
    };

    /**
     * Input definition for a list.
     * @public
     */
    const list$1 = {
        /**
         * The actual schema of the input, or a function that returns the schema.
         */
        schema: fragment('$slots.default'),
        /**
         * The type of node, can be a list, group, or input.
         */
        type: 'list',
        /**
         * An array of extra props to accept for this input.
         */
        props: [],
        /**
         * Additional features that should be added to your input
         */
        features: [disables],
    };

    /**
     * Input definition for a radio.
     * @public
     */
    const radio = {
        /**
         * The actual schema of the input, or a function that returns the schema.
         */
        schema: outer($if('$options == undefined', 
        /**
         * Single radio structure.
         */
        boxWrapper(inner(prefix(), box(), decorator(icon('decorator')), suffix()), $if('$label', boxLabel('$label'))), 
        /**
         * Multi radio structure.
         */
        fieldset(legend('$label'), help('$help'), boxOptions(boxOption(boxWrapper(inner(prefix(), $extend(box(), {
            bind: '$option.attrs',
            attrs: {
                id: '$option.attrs.id',
                value: '$option.value',
                checked: '$fns.isChecked($option.value)',
            },
        }), decorator(icon('decorator')), suffix()), $if('$option.label', boxLabel('$option.label'))), boxHelp('$option.help'))))), 
        // Help text only goes under the input when it is a single.
        $if('$options === undefined && $help', help('$help')), messages$1(message$1('$message.value'))),
        /**
         * The type of node, can be a list, group, or input.
         */
        type: 'input',
        /**
         * The family of inputs this one belongs too. For example "text" and "email"
         * are both part of the "text" family. This is primary used for styling.
         */
        family: 'box',
        /**
         * An array of extra props to accept for this input.
         */
        props: ['options', 'onValue', 'offValue', 'optionsLoader'],
        /**
         * Additional features that should be added to your input
         */
        features: [
            disables,
            options,
            radios,
            defaultIcon('decorator', 'radioDecorator'),
        ],
    };

    /**
     * Input definition for a select.
     * @public
     */
    const select = {
        /**
         * The actual schema of the input, or a function that returns the schema.
         */
        schema: outer(wrapper(label('$label'), inner(icon('prefix'), prefix(), selectInput$1($if('$slots.default', () => '$slots.default', $if('$slots.option', optionSlot, option('$option.label')))), $if('$attrs.multiple !== undefined', () => '', icon('select')), suffix(), icon('suffix'))), help('$help'), messages$1(message$1('$message.value'))),
        /**
         * The type of node, can be a list, group, or input.
         */
        type: 'input',
        /**
         * An array of extra props to accept for this input.
         */
        props: ['options', 'placeholder', 'optionsLoader'],
        /**
         * Additional features that should be added to your input
         */
        features: [options, select$1, defaultIcon('select', 'select')],
    };

    /**
     * Input definition for a textarea.
     * @public
     */
    const textarea = {
        /**
         * The actual schema of the input, or a function that returns the schema.
         */
        schema: outer(wrapper(label('$label'), inner(icon('prefix', 'label'), prefix(), textareaInput(), suffix(), icon('suffix'))), help('$help'), messages$1(message$1('$message.value'))),
        /**
         * The type of node, can be a list, group, or input.
         */
        type: 'input',
        /**
         * An array of extra props to accept for this input.
         */
        props: [],
        /**
         * Additional features that should be added to your input
         */
        features: [initialValue],
    };

    /**
     * Input definition for a text.
     * @public
     */
    const text = {
        /**
         * The actual schema of the input, or a function that returns the schema.
         */
        schema: outer(wrapper(label('$label'), inner(icon('prefix', 'label'), prefix(), textInput(), suffix(), icon('suffix'))), help('$help'), messages$1(message$1('$message.value'))),
        /**
         * The type of node, can be a list, group, or input.
         */
        type: 'input',
        /**
         * The family of inputs this one belongs too. For example "text" and "email"
         * are both part of the "text" family. This is primary used for styling.
         */
        family: 'text',
        /**
         * An array of extra props to accept for this input.
         */
        props: [],
        /**
         * Additional features that should be added to your input
         */
        features: [],
    };

    var index = /*#__PURE__*/Object.freeze({
        __proto__: null,
        button: button,
        submit: button,
        checkbox: checkbox,
        file: file,
        form: form,
        group: group,
        hidden: hidden,
        list: list$1,
        radio: radio,
        select: select,
        textarea: textarea,
        text: text,
        color: text,
        date: text,
        datetimeLocal: text,
        email: text,
        month: text,
        number: text,
        password: text,
        search: text,
        tel: text,
        time: text,
        url: text,
        week: text,
        range: text
    });

    let totalCreated = 1;
    /**
     * Determine if the given object is a vue component.
     *
     * @param obj - Object or function
     * @returns
     * @public
     */
    function isComponent(obj) {
        return ((typeof obj === 'function' && obj.length === 2) ||
            (typeof obj === 'object' &&
                !Array.isArray(obj) &&
                !('$el' in obj) &&
                !('$cmp' in obj) &&
                !('if' in obj)));
    }
    /**
     * Creates a new input from schema or a Vue component with the "standard"
     * FormKit features in place such as labels, help text, validation messages, and
     * class support.
     *
     * @param schemaOrComponent - The actual schema of the input or the component.
     * @param definitionOptions - Any options in the FormKitTypeDefinition you want
     * to define.
     *
     * @returns {@link @formkit/core#FormKitTypeDefinition | FormKitTypeDefinition}
     *
     * @public
     */
    function createInput(schemaOrComponent, definitionOptions = {}) {
        const definition = {
            type: 'input',
            ...definitionOptions,
        };
        let schema;
        if (isComponent(schemaOrComponent)) {
            const cmpName = `SchemaComponent${totalCreated++}`;
            schema = createSection('input', () => ({
                $cmp: cmpName,
                props: {
                    context: '$node.context',
                },
            }));
            definition.library = { [cmpName]: vue.markRaw(schemaOrComponent) };
        }
        else if (typeof schemaOrComponent === 'function') {
            schema = schemaOrComponent;
        }
        else {
            schema = createSection('input', () => cloneAny(schemaOrComponent));
        }
        // Use the default wrapping schema
        definition.schema = useSchema(schema || 'Schema undefined');
        return definition;
    }

    /**
     * @internal
     */
    const messages = createSection('messages', () => ({
        $el: 'ul',
        if: '$fns.length($messages)',
    }), true);
    /**
     * @internal
     */
    const message = createSection('message', () => ({
        $el: 'li',
        for: ['message', '$messages'],
        attrs: {
            key: '$message.key',
            id: `$id + '-' + $message.key`,
            'data-message-type': '$message.type',
        },
    }));
    /**
     * The actual schema to render for the messages.
     */
    const definition = messages(message('$message.value'));
    /**
     * Renders the messages for a parent node, or any node explicitly passed to it.
     * @public
     */
    const FormKitMessages = vue.defineComponent({
        props: {
            node: {
                type: Object,
                required: false,
            },
            sectionsSchema: {
                type: Object,
                default: {},
            },
            defaultPosition: {
                type: [String, Boolean],
                default: false,
            },
        },
        setup(props, context) {
            const node = vue.computed(() => {
                return props.node || vue.inject(parentSymbol, undefined);
            });
            vue.watch(node, () => {
                var _a;
                if (((_a = node.value) === null || _a === void 0 ? void 0 : _a.context) && !undefine(props.defaultPosition)) {
                    node.value.context.defaultMessagePlacement = false;
                }
            }, { immediate: true });
            const schema = definition(props.sectionsSchema || {});
            const data = vue.computed(() => {
                var _a, _b, _c, _d, _e, _f;
                return {
                    messages: ((_b = (_a = node.value) === null || _a === void 0 ? void 0 : _a.context) === null || _b === void 0 ? void 0 : _b.messages) || {},
                    fns: ((_d = (_c = node.value) === null || _c === void 0 ? void 0 : _c.context) === null || _d === void 0 ? void 0 : _d.fns) || {},
                    classes: ((_f = (_e = node.value) === null || _e === void 0 ? void 0 : _e.context) === null || _f === void 0 ? void 0 : _f.classes) || {},
                };
            });
            return () => {
                var _a;
                return ((_a = node.value) === null || _a === void 0 ? void 0 : _a.context)
                    ? vue.h(FormKitSchema, { schema, data: data.value }, { ...context.slots })
                    : null;
            };
        },
    });

    /**
     * Determine if the given input's value was should be considered "accepted".
     * @param context - The FormKitValidationContext
     * @public
     */
    const accepted = function accepted({ value }) {
        return ['yes', 'on', '1', 1, true, 'true'].includes(value);
    };
    accepted.skipEmpty = false;

    /**
     * Determine if the given input's value is after a given date.
     * Defaults to current time.
     * @param context - The FormKitValidationContext
     * @public
     */
    const date_after = function ({ value }, compare = false) {
        const timestamp = Date.parse(compare || new Date());
        const fieldValue = Date.parse(String(value));
        return isNaN(fieldValue) ? false : fieldValue > timestamp;
    };

    /**
     * Determine if the given input's value is only alpha characters.
     * @param context - The FormKitValidationContext
     * @public
     */
    const alpha = function ({ value }, set = 'default') {
        const sets = {
            default: /^[a-zA-ZÀ-ÖØ-öø-ÿĄąĆćČčĎďĘęĚěŁłŃńŇňŘřŚśŠšŤťŮůŹźŻŽžż]+$/,
            latin: /^[a-zA-Z]+$/,
        };
        const selectedSet = has(sets, set) ? set : 'default';
        return sets[selectedSet].test(String(value));
    };

    /**
     * Determine if the given input's value is only alpha characters.
     * @param context - The FormKitValidationContext
     * @public
     */
    const alpha_spaces = function ({ value }, set = 'default') {
        const sets = {
            default: /^[a-zA-ZÀ-ÖØ-öø-ÿĄąĆćČčĎďĘęĚěŁłŃńŇňŘřŚśŠšŤťŮůŹźŻŽžż ]+$/,
            latin: /^[a-zA-Z ]+$/,
        };
        const selectedSet = has(sets, set) ? set : 'default';
        return sets[selectedSet].test(String(value));
    };

    /**
     * Determine if the given input's value is only alpha or numeric characters.
     * @param context - The FormKitValidationContext
     * @public
     */
    const alphanumeric = function ({ value }, set = 'default') {
        const sets = {
            default: /^[a-zA-Z0-9À-ÖØ-öø-ÿĄąĆćĘęŁłŃńŚśŹźŻż]+$/,
            latin: /^[a-zA-Z0-9]+$/,
        };
        const selectedSet = has(sets, set) ? set : 'default';
        return sets[selectedSet].test(String(value));
    };

    /**
     * Determine if the given input's value before a given date.
     * @param context - The FormKitValidationContext
     * @public
     */
    const date_before = function ({ value }, compare = false) {
        const timestamp = Date.parse(compare || new Date());
        const fieldValue = Date.parse(String(value));
        return isNaN(fieldValue) ? false : fieldValue < timestamp;
    };

    /**
     * Determine if the given input's value is between two other values.
     * @param context - The FormKitValidationContext
     * @public
     */
    const between = function between({ value }, from, to) {
        if (!isNaN(value) && !isNaN(from) && !isNaN(to)) {
            const val = 1 * value;
            from = Number(from);
            to = Number(to);
            const [a, b] = from <= to ? [from, to] : [to, from];
            return val >= 1 * a && val <= 1 * b;
        }
        return false;
    };

    const hasConfirm = /(_confirm(?:ed)?)$/;
    /**
     * Determine if the given input's value matches another input's value
     * @param context - The FormKitValidationContext
     * @public
     */
    const confirm = function confirm(node, address, comparison = 'loose') {
        var _a;
        if (!address) {
            address = hasConfirm.test(node.name)
                ? node.name.replace(hasConfirm, '')
                : `${node.name}_confirm`;
        }
        const foreignValue = (_a = node.at(address)) === null || _a === void 0 ? void 0 : _a.value;
        return comparison === 'strict'
            ? node.value === foreignValue
            : node.value == foreignValue;
    };

    /**
     * Determine if the given input's value is between two other dates
     * @param context - The FormKitValidationContext
     * @public
     */
    const date_between = function date_between({ value }, dateA, dateB) {
        dateA = dateA instanceof Date ? dateA.getTime() : Date.parse(dateA);
        dateB = dateB instanceof Date ? dateB.getTime() : Date.parse(dateB);
        const compareTo = value instanceof Date ? value.getTime() : Date.parse(String(value));
        if (dateA && !dateB) {
            dateB = dateA;
            dateA = Date.now();
        }
        else if (!dateA || !compareTo) {
            return false;
        }
        return compareTo >= dateA && compareTo <= dateB;
    };

    /**
     * Determine if the given input's value matches a specified date format
     * @param context - The FormKitValidationContext
     * @public
     */
    const date_format = function date({ value }, format) {
        if (format && typeof format === 'string') {
            return regexForFormat(format).test(String(value));
        }
        return !isNaN(Date.parse(String(value)));
    };

    /**
     * Determine if the given input's value is a plausible email address.
     * @param context - The FormKitValidationContext
     * @public
     */
    const email = function email({ value }) {
        const isEmail = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
        return isEmail.test(String(value));
    };

    /**
     * Determine if the given input's value ends with a given string
     * @param context - The FormKitValidationContext
     * @public
     */
    const ends_with = function ends_with({ value }, ...stack) {
        if (typeof value === 'string' && stack.length) {
            return stack.some((item) => {
                return value.endsWith(item);
            });
        }
        else if (typeof value === 'string' && stack.length === 0) {
            return true;
        }
        return false;
    };

    /**
     * Determine if the given input's value is in a set of possible values.
     * @param context - The FormKitValidationContext
     * @public
     */
    const is = function is({ value }, ...stack) {
        return stack.some((item) => {
            if (typeof item === 'object') {
                return eq(item, value);
            }
            return item == value;
        });
    };

    /**
     * Determine if the given input's value length is full width.
     * @param context - The FormKitValidationContext
     * @public
     */
    const length = function length({ value }, first = 0, second = Infinity) {
        first = parseInt(first);
        second = isNaN(parseInt(second)) ? Infinity : parseInt(second);
        const min = first <= second ? first : second;
        const max = second >= first ? second : first;
        if (typeof value === 'string' || Array.isArray(value)) {
            return value.length >= min && value.length <= max;
        }
        else if (value && typeof value === 'object') {
            const length = Object.keys(value).length;
            return length >= min && length <= max;
        }
        return false;
    };

    /**
     * Determine if the given input's value matches one or more values or regular expressions.
     * @param context - The FormKitValidationContext
     * @public
     */
    const matches = function matches({ value }, ...stack) {
        return stack.some((pattern) => {
            if (typeof pattern === 'string' &&
                pattern.substr(0, 1) === '/' &&
                pattern.substr(-1) === '/') {
                pattern = new RegExp(pattern.substr(1, pattern.length - 2));
            }
            if (pattern instanceof RegExp) {
                return pattern.test(String(value));
            }
            return pattern === value;
        });
    };

    /**
     * Determine if the given input's value is less than the maximum
     * @param context - The FormKitValidationContext
     * @public
     */
    const max = function max({ value }, maximum = 10) {
        if (Array.isArray(value)) {
            return value.length <= maximum;
        }
        return Number(value) <= Number(maximum);
    };

    /**
     * Determine if the given input's value is less than a certain value.
     * @param context - The FormKitValidationContext
     * @public
     */
    const min = function min({ value }, minimum = 1) {
        if (Array.isArray(value)) {
            return value.length >= minimum;
        }
        return Number(value) >= Number(minimum);
    };

    /**
     * Determine if the given input's value is not in a given stack
     * @param context - The FormKitValidationContext
     * @public
     */
    const not = function not({ value }, ...stack) {
        return !stack.some((item) => {
            if (typeof item === 'object') {
                return eq(item, value);
            }
            return item === value;
        });
    };

    /**
     * Determine if the given input's value is numeric
     * @param context - The FormKitValidationContext
     * @public
     */
    const number = function number({ value }) {
        return !isNaN(value);
    };

    /**
     * Determine if the given input's value was required.
     * @param context - The FormKitValidationContext
     * @public
     */
    const required = function required({ value }, action = 'default') {
        return action === 'trim' && typeof value === 'string'
            ? !empty(value.trim())
            : !empty(value);
    };
    /**
     * This rules should run even if the inputs is empty (obviously)
     */
    required.skipEmpty = false;

    /**
     * Determine if the given input's value starts with a substring
     * @param context - The FormKitValidationContext
     * @public
     */
    const starts_with = function starts_with({ value }, ...stack) {
        if (typeof value === 'string' && stack.length) {
            return stack.some((item) => {
                return value.startsWith(item);
            });
        }
        else if (typeof value === 'string' && stack.length === 0) {
            return true;
        }
        return false;
    };

    /**
     * Determine if the given input's value is an http url
     * @param context - The FormKitValidationContext
     * @public
     */
    const url = function url({ value }, ...stack) {
        try {
            const protocols = stack.length ? stack : ['http:', 'https:'];
            const url = new URL(String(value));
            return protocols.includes(url.protocol);
        }
        catch {
            return false;
        }
    };

    var defaultRules = /*#__PURE__*/Object.freeze({
        __proto__: null,
        accepted: accepted,
        alpha: alpha,
        alpha_spaces: alpha_spaces,
        alphanumeric: alphanumeric,
        between: between,
        confirm: confirm,
        date_after: date_after,
        date_before: date_before,
        date_between: date_between,
        date_format: date_format,
        email: email,
        ends_with: ends_with,
        is: is,
        length: length,
        matches: matches,
        max: max,
        min: min,
        not: not,
        number: number,
        required: required,
        starts_with: starts_with,
        url: url
    });

    /**
     * FormKit Observer is a utility to wrap a FormKitNode in a dependency tracking observer proxy.
     *
     * @packageDocumentation
     */
    /**
     * A registry of all revoked observers.
     */
    const revokedObservers = new WeakSet();
    /**
     * Creates the observer.
     * @param node - The {@link @formkit/core#FormKitNode | FormKitNode} to observe.
     * @param dependencies - The dependent nodes and the events that are required to
     * watch for changes.
     * @returns Returns a {@link @formkit/observer#FormKitObservedNode | FormKitObservedNode}.
     * @public
     */
    function createObserver(node, dependencies) {
        // The dependencies touched during tracking
        const deps = dependencies || Object.assign(new Map(), { active: false });
        // A registry of event receipts returned by the event system
        const receipts = new Map();
        /**
         * Simple function to add a dependency to the deps map.
         * @param event - The name of the event type (like commit/input etc)
         */
        const addDependency = function (event) {
            var _a;
            if (!deps.active)
                return;
            if (!deps.has(node))
                deps.set(node, new Set());
            (_a = deps.get(node)) === null || _a === void 0 ? void 0 : _a.add(event);
        };
        /**
         * Proxies the props of a node so we know which ones were messed with, could
         * potentially be more generalized in the future if we want to support
         * more sub-objects.
         * @param props - The props object from a node
         * @returns
         */
        const observeProps = function (props) {
            return new Proxy(props, {
                get(...args) {
                    typeof args[1] === 'string' && addDependency(`prop:${args[1]}`);
                    return Reflect.get(...args);
                },
            });
        };
        /**
         * Observes the FormKit ledger "value".
         * @param ledger - A formkit ledger counter.
         */
        const observeLedger = function (ledger) {
            return new Proxy(ledger, {
                get(...args) {
                    if (args[1] === 'value') {
                        return (key) => {
                            addDependency(`count:${key}`);
                            return ledger.value(key);
                        };
                    }
                    return Reflect.get(...args);
                },
            });
        };
        /**
         * Return values from our observer proxy first pass through this function
         * which gives us a chance to listen sub-dependencies and properties.
         */
        const observe = function (value, property) {
            if (isNode(value)) {
                return createObserver(value, deps);
            }
            if (property === 'value')
                addDependency('commit');
            if (property === 'props')
                return observeProps(value);
            if (property === 'ledger')
                return observeLedger(value);
            return value;
        };
        /**
         * The actual proxy object of the original node.
         */
        const { proxy: observed, revoke, } = Proxy.revocable(node, {
            get(...args) {
                switch (args[1]) {
                    case '_node':
                        return node;
                    case 'deps':
                        return deps;
                    case 'watch':
                        return (block, after) => watch(observed, block, after);
                    case 'observe':
                        return () => {
                            const old = new Map(deps);
                            deps.clear();
                            deps.active = true;
                            return old;
                        };
                    case 'stopObserve':
                        return () => {
                            const newDeps = new Map(deps);
                            deps.active = false;
                            return newDeps;
                        };
                    case 'receipts':
                        return receipts;
                    case 'kill':
                        return () => {
                            removeListeners(receipts);
                            revokedObservers.add(args[2]);
                            revoke();
                            return undefined;
                        };
                }
                const value = Reflect.get(...args);
                // If we're dealing with a function, we need to sub-call the function
                // get that return value, and pass it through the same logic.
                if (typeof value === 'function') {
                    return (...subArgs) => {
                        const subValue = value(...subArgs);
                        return observe(subValue, args[1]);
                    };
                }
                return observe(value, args[1]);
            },
        });
        return observed;
    }
    /**
     * Given two maps (`toAdd` and `toRemove`), apply the dependencies as event
     * listeners on the underlying nodes.
     * @param node - The node to apply dependencies to.
     * @param callback - The callback to add or remove.
     * @internal
     */
    function applyListeners(node, [toAdd, toRemove], callback) {
        toAdd.forEach((events, depNode) => {
            events.forEach((event) => {
                var _a;
                node.receipts.has(depNode) || node.receipts.set(depNode, {});
                node.receipts.set(depNode, Object.assign((_a = node.receipts.get(depNode)) !== null && _a !== void 0 ? _a : {}, {
                    [event]: depNode.on(event, callback),
                }));
            });
        });
        toRemove.forEach((events, depNode) => {
            events.forEach((event) => {
                if (node.receipts.has(depNode)) {
                    const nodeReceipts = node.receipts.get(depNode);
                    if (nodeReceipts && has(nodeReceipts, event)) {
                        depNode.off(nodeReceipts[event]);
                        delete nodeReceipts[event];
                        node.receipts.set(depNode, nodeReceipts);
                    }
                }
            });
        });
    }
    /**
     * Remove all the receipts from the observed node and subtree.
     * @param receipts - The FormKit observer receipts to remove.
     * @public
     */
    function removeListeners(receipts) {
        receipts.forEach((events, node) => {
            for (const event in events) {
                node.off(events[event]);
            }
        });
    }
    /**
     * Observes a chunk of code to dependencies, and then re-calls that chunk of
     * code when those dependencies are manipulated.
     * @param node - The node to observer
     * @param block - The block of code to observe
     * @public
     */
    function watch(node, block, after) {
        const doAfterObservation = (res) => {
            const newDeps = node.stopObserve();
            applyListeners(node, diffDeps(oldDeps, newDeps), () => watch(node, block, after));
            if (after)
                after(res);
        };
        const oldDeps = new Map(node.deps);
        node.observe();
        const res = block(node);
        if (res instanceof Promise)
            res.then((val) => doAfterObservation(val));
        else
            doAfterObservation(res);
    }
    /**
     * Determines which nodes should be added as dependencies and which should be
     * removed.
     * @param previous - The previous watcher dependencies.
     * @param current - The new/current watcher dependencies.
     * @returns A tuple of maps: `toAdd` and `toRemove`.
     * @public
     */
    function diffDeps(previous, current) {
        const toAdd = new Map();
        const toRemove = new Map();
        current.forEach((events, node) => {
            if (!previous.has(node)) {
                toAdd.set(node, events);
            }
            else {
                const eventsToAdd = new Set();
                const previousEvents = previous.get(node);
                events.forEach((event) => !(previousEvents === null || previousEvents === void 0 ? void 0 : previousEvents.has(event)) && eventsToAdd.add(event));
                toAdd.set(node, eventsToAdd);
            }
        });
        previous.forEach((events, node) => {
            if (!current.has(node)) {
                toRemove.set(node, events);
            }
            else {
                const eventsToRemove = new Set();
                const newEvents = current.get(node);
                events.forEach((event) => !(newEvents === null || newEvents === void 0 ? void 0 : newEvents.has(event)) && eventsToRemove.add(event));
                toRemove.set(node, eventsToRemove);
            }
        });
        return [toAdd, toRemove];
    }
    /**
     * Checks if the given node is revoked.
     * @param node - Any observed node to check.
     * @returns A `boolean` indicating if the node is revoked.
     * @public
     */
    function isKilled(node) {
        return revokedObservers.has(node);
    }

    /**
     * Message that gets set when the node is awaiting validation.
     */
    const validatingMessage = createMessage({
        type: 'state',
        blocking: true,
        visible: false,
        value: true,
        key: 'validating',
    });
    /**
     * The actual validation plugin function. Everything must be bootstrapped here.
     * @param baseRules - Base validation rules to include in the plugin. By default,
     * FormKit makes all rules in the @formkit/rules package available via the
     * defaultConfig.
     * @public
     */
    function createValidationPlugin(baseRules = {}) {
        return function validationPlugin(node) {
            let propRules = cloneAny(node.props.validationRules || {});
            let availableRules = { ...baseRules, ...propRules };
            // create an observed node
            let observedNode = createObserver(node);
            const state = { input: token(), rerun: null, isPassing: true };
            let validation = cloneAny(node.props.validation);
            // If the node's validation props change, reboot:
            node.on('prop:validation', ({ payload }) => reboot(payload, availableRules));
            node.on('prop:validationRules', ({ payload }) => reboot(validation, payload));
            /**
             * Reboots the validation using new rules or declarations/intents.
             * @param newValidation - New validation declaration to use
             * @param newRules - New validation rules to use
             * @returns
             */
            function reboot(newValidation, newRules) {
                var _a;
                if (eq(Object.keys(propRules || {}), Object.keys(newRules || {})) &&
                    eq(validation, newValidation))
                    return;
                propRules = cloneAny(newRules);
                validation = cloneAny(newValidation);
                availableRules = { ...baseRules, ...propRules };
                // Destroy all observers that may re-trigger validation on an old stack
                removeListeners(observedNode.receipts);
                // Clear existing message observers
                (_a = node.props.parsedRules) === null || _a === void 0 ? void 0 : _a.forEach((validation) => {
                    var _a;
                    validation.messageObserver = (_a = validation.messageObserver) === null || _a === void 0 ? void 0 : _a.kill();
                });
                // Remove all existing messages before re-validating
                node.store.filter(() => false, 'validation');
                node.props.parsedRules = parseRules(newValidation, availableRules);
                observedNode.kill();
                observedNode = createObserver(node);
                validate(observedNode, node.props.parsedRules, state);
            }
            // Validate the field when this plugin is initialized
            node.props.parsedRules = parseRules(validation, availableRules);
            validate(observedNode, node.props.parsedRules, state);
        };
    }
    /**
     * Given parsed validations, a value and a node, run the validations and set
     * the appropriate store messages on the node.
     * @param value - The value being validated
     * @param node - The Node this value belongs to
     * @param rules - The rules
     */
    function validate(node, validations, state) {
        if (isKilled(node))
            return;
        state.input = token();
        state.isPassing = true;
        node.store.filter((message) => !message.meta.removeImmediately, 'validation');
        validations.forEach((validation) => validation.debounce && clearTimeout(validation.timer));
        if (validations.length) {
            node.store.set(validatingMessage);
            run(0, validations, node, state, false, () => {
                node.store.remove(validatingMessage.key);
            });
        }
    }
    /**
     * Runs validation rules recursively while collecting dependencies allowing for
     * cross-node validation rules that automatically re-trigger when a foreign
     * value is changed.
     * @param current - The index of the current validation rule
     * @param validations - The remaining validation rule stack to run
     * @param node - An observed node, the owner of this validation stack
     * @param state - An object of state information about this run
     * @param removeImmediately - Should messages created during this call be removed immediately when a new commit takes place?
     * @returns
     */
    function run(current, validations, node, state, removeImmediately, complete) {
        const validation = validations[current];
        if (!validation)
            return complete();
        const currentRun = state.input;
        validation.state = null;
        function next(async, result) {
            state.isPassing = state.isPassing && !!result;
            validation.queued = false;
            const newDeps = node.stopObserve();
            applyListeners(node, diffDeps(validation.deps, newDeps), () => {
                validation.queued = true;
                if (state.rerun)
                    clearTimeout(state.rerun);
                state.rerun = setTimeout(validate, 0, node, validations, state);
            });
            validation.deps = newDeps;
            if (state.input === currentRun) {
                validation.state = result;
                if (result === false) {
                    createFailedMessage(node, validation, removeImmediately || async);
                }
                else {
                    removeMessage(node, validation);
                }
                if (validations.length > current + 1) {
                    run(current + 1, validations, node, state, removeImmediately || async, complete);
                }
                else {
                    // The validation has completed
                    complete();
                }
            }
        }
        if ((!empty(node.value) || !validation.skipEmpty) &&
            (state.isPassing || validation.force)) {
            if (validation.queued) {
                runRule(validation, node, (result) => {
                    result instanceof Promise
                        ? result.then((r) => next(true, r))
                        : next(false, result);
                });
            }
            else {
                // In this case our rule is not queued, so literally nothing happened that
                // would affect it, we just need to move past this rule and make no
                // modifications to state
                run(current + 1, validations, node, state, removeImmediately, complete);
            }
        }
        else {
            // This rule is not being run because either:
            //  1. The field is empty and this rule should not run when empty
            //  2. A previous validation rule is failing and this one is not forced
            // In this case we should call next validation.
            if (empty(node.value) && validation.skipEmpty && state.isPassing) {
                // This node has an empty value so its validation was skipped. So we
                // need to queue it up, we do that by starting an observation and just
                // touching the value attribute.
                node.observe();
                node.value;
                // Because this validation rule is skipped when the node's value is empty
                // so we keep the current value `state.isPassing` to the next rule execution
                // if we pass null it will be typecasted to false and all following rules
                // will be ignored including `required` rule which cause odds behavior
                next(false, state.isPassing);
            }
            else {
                next(false, null);
            }
        }
    }
    /**
     * Run a validation rule debounced or not.
     * @param validation - A validation to debounce
     */
    function runRule(validation, node, after) {
        if (validation.debounce) {
            validation.timer = setTimeout(() => {
                node.observe();
                after(validation.rule(node, ...validation.args));
            }, validation.debounce);
        }
        else {
            node.observe();
            after(validation.rule(node, ...validation.args));
        }
    }
    /**
     * The messages given to this function have already been set on the node, but
     * any other validation messages on the node that are not included in this
     * stack should be removed because they have been resolved.
     * @param node - The node to operate on.
     * @param messages - A new stack of messages
     */
    function removeMessage(node, validation) {
        const key = `rule_${validation.name}`;
        if (validation.messageObserver) {
            validation.messageObserver = validation.messageObserver.kill();
        }
        if (has(node.store, key)) {
            node.store.remove(key);
        }
    }
    /**
     *
     * @param value - The value that is failing
     * @param validation - The validation object
     */
    function createFailedMessage(node, validation, removeImmediately) {
        if (isKilled(node))
            return;
        if (!validation.messageObserver) {
            validation.messageObserver = createObserver(node._node);
        }
        validation.messageObserver.watch((node) => {
            const i18nArgs = createI18nArgs(node, validation);
            return i18nArgs;
        }, (i18nArgs) => {
            const customMessage = createCustomMessage(node, validation, i18nArgs);
            // Here we short circuit the i18n system to force the output.
            const message = createMessage({
                blocking: validation.blocking,
                key: `rule_${validation.name}`,
                meta: {
                    /**
                     * Use this key instead of the message root key to produce i18n validation
                     * messages.
                     */
                    messageKey: validation.name,
                    /**
                     * For messages that were created *by or after* a debounced or async
                     * validation rule — we make note of it so we can immediately remove them
                     * as soon as the next commit happens.
                     */
                    removeImmediately,
                    /**
                     * Determines if this message should be passed to localization.
                     */
                    localize: !customMessage,
                    /**
                     * The arguments that will be passed to the validation rules
                     */
                    i18nArgs,
                },
                type: 'validation',
                value: customMessage || 'This field is not valid.',
            });
            node.store.set(message);
        });
    }
    /**
     * Returns a custom validation message if applicable.
     * @param node - FormKit Node
     * @param validation - The validation rule being processed.
     */
    function createCustomMessage(node, validation, i18nArgs) {
        const customMessage = node.props.validationMessages &&
            has(node.props.validationMessages, validation.name)
            ? node.props.validationMessages[validation.name]
            : undefined;
        if (typeof customMessage === 'function') {
            return customMessage(...i18nArgs);
        }
        return customMessage;
    }
    /**
     * Creates the arguments passed to the i18n
     * @param node - The node that performed the validation
     * @param validation - The validation that failed
     */
    function createI18nArgs(node, validation) {
        // If a custom message has been found, short circuit the i18n system.
        return [
            {
                node,
                name: createMessageName(node),
                args: validation.args,
            },
        ];
    }
    /**
     * Given a node, this returns the name that should be used in validation
     * messages. This is either the `validationLabel` prop, the `label` prop, or
     * the name of the input (in that order).
     * @param node - The node to display
     * @returns
     * @public
     */
    function createMessageName(node) {
        if (typeof node.props.validationLabel === 'function') {
            return node.props.validationLabel(node);
        }
        return (node.props.validationLabel ||
            node.props.label ||
            node.props.name ||
            String(node.name));
    }
    /**
     * Describes hints, must also be changed in the debounceExtractor.
     */
    const hintPattern = '(?:[\\*+?()0-9]+)';
    /**
     * A pattern to describe rule names. Rules names can only contain letters,
     * numbers, and underscores and must start with a letter.
     */
    const rulePattern = '[a-zA-Z][a-zA-Z0-9_]+';
    /**
     * Regular expression for extracting rule data.
     */
    const ruleExtractor = new RegExp(`^(${hintPattern}?${rulePattern})(?:\\:(.*)+)?$`, 'i');
    /**
     * Validation hints are special characters preceding a validation rule, like
     * !phone
     */
    const hintExtractor = new RegExp(`^(${hintPattern})(${rulePattern})$`, 'i');
    /**
     * Given a hint string like ^(200)? or ^? or (200)?^ extract the hints to
     * matches.
     */
    const debounceExtractor = /([\*+?]+)?(\(\d+\))([\*+?]+)?/;
    /**
     * Determines if a given string is in the proper debounce format.
     */
    const hasDebounce = /\(\d+\)/;
    /**
     * The default values of the available validation hints.
     */
    const defaultHints = {
        blocking: true,
        debounce: 0,
        force: false,
        skipEmpty: true,
        name: '',
    };
    /**
     * Parse validation intents and strings into validation rule stacks.
     * @param validation - Either a string a validation rules, or proper array of structured rules.
     * @internal
     */
    function parseRules(validation, rules) {
        if (!validation)
            return [];
        const intents = typeof validation === 'string'
            ? extractRules(validation)
            : clone(validation);
        return intents.reduce((validations, args) => {
            let rule = args.shift();
            const hints = {};
            if (typeof rule === 'string') {
                const [ruleName, parsedHints] = parseHints(rule);
                if (has(rules, ruleName)) {
                    rule = rules[ruleName];
                    Object.assign(hints, parsedHints);
                }
            }
            if (typeof rule === 'function') {
                validations.push({
                    rule,
                    args,
                    timer: 0,
                    state: null,
                    queued: true,
                    deps: new Map(),
                    ...defaultHints,
                    ...fnHints(hints, rule),
                });
            }
            return validations;
        }, []);
    }
    /**
     * A string of validation rules written in FormKitRule notation.
     * @param validation - The string of rules
     * @internal
     */
    function extractRules(validation) {
        return validation.split('|').reduce((rules, rule) => {
            const parsedRule = parseRule(rule);
            if (parsedRule) {
                rules.push(parsedRule);
            }
            return rules;
        }, []);
    }
    /**
     * Given a rule like confirm:password_confirm produce a FormKitValidationIntent
     * @param rule - A string representing a validation rule.
     * @returns
     */
    function parseRule(rule) {
        const trimmed = rule.trim();
        if (trimmed) {
            const matches = trimmed.match(ruleExtractor);
            if (matches && typeof matches[1] === 'string') {
                const ruleName = matches[1].trim();
                const args = matches[2] && typeof matches[2] === 'string'
                    ? matches[2].split(',').map((s) => s.trim())
                    : [];
                return [ruleName, ...args];
            }
        }
        return false;
    }
    /**
     * Given a rule name, detect if there are any additional hints like !
     * @param ruleName - string representing a rule name
     * @returns
     */
    function parseHints(ruleName) {
        const matches = ruleName.match(hintExtractor);
        if (!matches) {
            return [ruleName, { name: ruleName }];
        }
        const map = {
            '*': { force: true },
            '+': { skipEmpty: false },
            '?': { blocking: false },
        };
        const [, hints, rule] = matches;
        const hintGroups = hasDebounce.test(hints)
            ? hints.match(debounceExtractor) || []
            : [, hints];
        return [
            rule,
            [hintGroups[1], hintGroups[2], hintGroups[3]].reduce((hints, group) => {
                if (!group)
                    return hints;
                if (hasDebounce.test(group)) {
                    hints.debounce = parseInt(group.substr(1, group.length - 1));
                }
                else {
                    group
                        .split('')
                        .forEach((hint) => has(map, hint) && Object.assign(hints, map[hint]));
                }
                return hints;
            }, { name: rule }),
        ];
    }
    /**
     * Extracts hint properties from the validation rule function itself and applies
     * them if they are not already in the set of validation hints extracted from
     * strings.
     * @param existingHints - An existing set of hints already parsed
     * @param rule - The actual rule function, which can contain hint properties
     * @returns
     */
    function fnHints(existingHints, rule) {
        if (!existingHints.name) {
            existingHints.name = rule.ruleName || rule.name;
        }
        return ['skipEmpty', 'force', 'debounce', 'blocking'].reduce((hints, hint) => {
            if (has(rule, hint) && !has(hints, hint)) {
                Object.assign(hints, {
                    [hint]: rule[hint],
                });
            }
            return hints;
        }, existingHints);
    }

    /**
     * Given a string, convert it to sentence case.
     *
     * @param str - The string to sentence case.
     *
     * @returns `string`
     *
     * @public
     */
    function sentence(str) {
        return str[0].toUpperCase() + str.substr(1);
    }
    /**
     * Creates an oxford-comma separated list of items.
     *
     * @param items - the items to list out.
     * @param conjunction - in the list "x, y, and z", "and" is the conjunction.
     * Defaults to "or".
     *
     * @returns `string`
     *
     * @public
     */
    function list(items, conjunction = 'or') {
        return items.reduce((oxford, item, index) => {
            oxford += item;
            if (index <= items.length - 2 && items.length > 2) {
                oxford += ', ';
            }
            if (index === items.length - 2) {
                oxford += `${items.length === 2 ? ' ' : ''}${conjunction} `;
            }
            return oxford;
        }, '');
    }
    /**
     * Given a string or a date, return a nice human-readable version.
     *
     * @param date - A string or a date.
     *
     * @returns `string`
     *
     * @public
     */
    function date(date) {
        const dateTime = typeof date === 'string' ? new Date(Date.parse(date)) : date;
        if (!(dateTime instanceof Date)) {
            return '(unknown)';
        }
        return new Intl.DateTimeFormat(undefined, {
            dateStyle: 'medium',
            timeZone: 'UTC'
        }).format(dateTime);
    }
    /**
     * Orders two variables from smallest to largest.
     *
     * @param first - The first number or string.
     * @param second - The second number or string.
     *
     * @returns `[smaller: number | string, larger: number | string]`
     *
     * @public
     */
    function order(first, second) {
        return Number(first) >= Number(second) ? [second, first] : [first, second];
    }

    /**
     * Here we can import additional helper functions to assist in formatting our
     * language. Feel free to add additional helper methods to libs/formats if it
     * assists in creating good validation messages for your locale.
     */
    /**
     * Standard language for interface features.
     * @public
     */
    const ui$u = {
        /**
         * Shown on a button for adding additional items.
         */
        add: 'Add',
        /**
         * Shown when a button to remove items is visible.
         */
        remove: 'Remove',
        /**
         * Shown when there are multiple items to remove at the same time.
         */
        removeAll: 'Remove all',
        /**
         * Shown when all fields are not filled out correctly.
         */
        incomplete: 'Sorry, not all fields are filled out correctly.',
        /**
         * Shown in a button inside a form to submit the form.
         */
        submit: 'Submit',
        /**
         * Shown when no files are selected.
         */
        noFiles: 'No file chosen',
        /**
         * Shown on buttons that move fields up in a list.
         */
        moveUp: 'Move up',
        /**
         * Shown on buttons that move fields down in a list.
         */
        moveDown: 'Move down',
        /**
         * Shown when something is actively loading.
         */
        isLoading: 'Loading...',
        /**
         * Shown when there is more to load.
         */
        loadMore: 'Load more',
        /**
         * Show on buttons that navigate state forward
         */
        next: 'Next',
        /**
         * Show on buttons that navigate state backward
         */
        prev: 'Previous',
    };
    /**
     * These are all the possible strings that pertain to validation messages.
     * @public
     */
    const validation$u = {
        /**
         * The value is not an accepted value.
         * @see {@link https://formkit.com/essentials/validation#accepted}
         */
        accepted({ name }) {
            /* <i18n case="Shown when the user-provided value is not a valid 'accepted' value."> */
            return `Please accept the ${name}.`;
            /* </i18n> */
        },
        /**
         * The date is not after
         * @see {@link https://formkit.com/essentials/validation#date-after}
         */
        date_after({ name, args }) {
            if (Array.isArray(args) && args.length) {
                /* <i18n case="Shown when the user-provided date is not after the date supplied to the rule."> */
                return `${sentence(name)} must be after ${date(args[0])}.`;
                /* </i18n> */
            }
            /* <i18n case="Shown when the user-provided date is not after today's date, since no date was supplied to the rule."> */
            return `${sentence(name)} must be in the future.`;
            /* </i18n> */
        },
        /**
         * The value is not a letter.
         * @see {@link https://formkit.com/essentials/validation#alpha}
         */
        alpha({ name }) {
            /* <i18n case="Shown when the user-provided value contains non-alphabetical characters."> */
            return `${sentence(name)} can only contain alphabetical characters.`;
            /* </i18n> */
        },
        /**
         * The value is not alphanumeric
         * @see {@link https://formkit.com/essentials/validation#alphanumeric}
         */
        alphanumeric({ name }) {
            /* <i18n case="Shown when the user-provided value contains non-alphanumeric characters."> */
            return `${sentence(name)} can only contain letters and numbers.`;
            /* </i18n> */
        },
        /**
         * The value is not letter and/or spaces
         * @see {@link https://formkit.com/essentials/validation#alpha-spaces}
         */
        alpha_spaces({ name }) {
            /* <i18n case="Shown when the user-provided value contains non-alphabetical and non-space characters."> */
            return `${sentence(name)} can only contain letters and spaces.`;
            /* </i18n> */
        },
        /**
         * The date is not before
         * @see {@link https://formkit.com/essentials/validation#date-before}
         */
        date_before({ name, args }) {
            if (Array.isArray(args) && args.length) {
                /* <i18n case="Shown when the user-provided date is not before the date supplied to the rule."> */
                return `${sentence(name)} must be before ${date(args[0])}.`;
                /* </i18n> */
            }
            /* <i18n case="Shown when the user-provided date is not before today's date, since no date was supplied to the rule."> */
            return `${sentence(name)} must be in the past.`;
            /* </i18n> */
        },
        /**
         * The value is not between two numbers
         * @see {@link https://formkit.com/essentials/validation#between}
         */
        between({ name, args }) {
            if (isNaN(args[0]) || isNaN(args[1])) {
                /* <i18n case="Shown when any of the arguments supplied to the rule were not a number."> */
                return `This field was configured incorrectly and can’t be submitted.`;
                /* </i18n> */
            }
            const [a, b] = order(args[0], args[1]);
            /* <i18n case="Shown when the user-provided value is not between two numbers."> */
            return `${sentence(name)} must be between ${a} and ${b}.`;
            /* </i18n> */
        },
        /**
         * The confirmation field does not match
         * @see {@link https://formkit.com/essentials/validation#confirm}
         */
        confirm({ name }) {
            /* <i18n case="Shown when the user-provided value does not equal the value of the matched input."> */
            return `${sentence(name)} does not match.`;
            /* </i18n> */
        },
        /**
         * The value is not a valid date
         * @see {@link https://formkit.com/essentials/validation#date-format}
         */
        date_format({ name, args }) {
            if (Array.isArray(args) && args.length) {
                /* <i18n case="Shown when the user-provided date does not satisfy the date format supplied to the rule."> */
                return `${sentence(name)} is not a valid date, please use the format ${args[0]}`;
                /* </i18n> */
            }
            /* <i18n case="Shown when no date argument was supplied to the rule."> */
            return 'This field was configured incorrectly and can’t be submitted';
            /* </i18n> */
        },
        /**
         * Is not within expected date range
         * @see {@link https://formkit.com/essentials/validation#date-between}
         */
        date_between({ name, args }) {
            /* <i18n case="Shown when the user-provided date is not between the start and end dates supplied to the rule. "> */
            return `${sentence(name)} must be between ${date(args[0])} and ${date(args[1])}`;
            /* </i18n> */
        },
        /**
         * Shown when the user-provided value is not a valid email address.
         * @see {@link https://formkit.com/essentials/validation#email}
         */
        email: 'Please enter a valid email address.',
        /**
         * Does not end with the specified value
         * @see {@link https://formkit.com/essentials/validation#ends-with}
         */
        ends_with({ name, args }) {
            /* <i18n case="Shown when the user-provided value does not end with the substring supplied to the rule."> */
            return `${sentence(name)} doesn’t end with ${list(args)}.`;
            /* </i18n> */
        },
        /**
         * Is not an allowed value
         * @see {@link https://formkit.com/essentials/validation#is}
         */
        is({ name }) {
            /* <i18n case="Shown when the user-provided value is not one of the values supplied to the rule."> */
            return `${sentence(name)} is not an allowed value.`;
            /* </i18n> */
        },
        /**
         * Does not match specified length
         * @see {@link https://formkit.com/essentials/validation#length}
         */
        length({ name, args: [first = 0, second = Infinity] }) {
            const min = Number(first) <= Number(second) ? first : second;
            const max = Number(second) >= Number(first) ? second : first;
            if (min == 1 && max === Infinity) {
                /* <i18n case="Shown when the length of the user-provided value is not at least one character."> */
                return `${sentence(name)} must be at least one character.`;
                /* </i18n> */
            }
            if (min == 0 && max) {
                /* <i18n case="Shown when first argument supplied to the rule is 0, and the user-provided value is longer than the max (the 2nd argument) supplied to the rule."> */
                return `${sentence(name)} must be less than or equal to ${max} characters.`;
                /* </i18n> */
            }
            if (min === max) {
                /* <i18n case="Shown when first and second argument supplied to the rule are the same, and the user-provided value is not any of the arguments supplied to the rule."> */
                return `${sentence(name)} should be ${max} characters long.`;
                /* </i18n> */
            }
            if (min && max === Infinity) {
                /* <i18n case="Shown when the length of the user-provided value is less than the minimum supplied to the rule and there is no maximum supplied to the rule."> */
                return `${sentence(name)} must be greater than or equal to ${min} characters.`;
                /* </i18n> */
            }
            /* <i18n case="Shown when the length of the user-provided value is between the two lengths supplied to the rule."> */
            return `${sentence(name)} must be between ${min} and ${max} characters.`;
            /* </i18n> */
        },
        /**
         * Value is not a match
         * @see {@link https://formkit.com/essentials/validation#matches}
         */
        matches({ name }) {
            /* <i18n case="Shown when the user-provided value does not match any of the values or RegExp patterns supplied to the rule. "> */
            return `${sentence(name)} is not an allowed value.`;
            /* </i18n> */
        },
        /**
         * Exceeds maximum allowed value
         * @see {@link https://formkit.com/essentials/validation#max}
         */
        max({ name, node: { value }, args }) {
            if (Array.isArray(value)) {
                /* <i18n case="Shown when the length of the array of user-provided values is longer than the max supplied to the rule."> */
                return `Cannot have more than ${args[0]} ${name}.`;
                /* </i18n> */
            }
            /* <i18n case="Shown when the user-provided value is greater than the maximum number supplied to the rule."> */
            return `${sentence(name)} must be less than or equal to ${args[0]}.`;
            /* </i18n> */
        },
        /**
         * The (field-level) value does not match specified mime type
         * @see {@link https://formkit.com/essentials/validation#mime}
         */
        mime({ name, args }) {
            if (!args[0]) {
                /* <i18n case="Shown when no file formats were supplied to the rule."> */
                return 'No file formats allowed.';
                /* </i18n> */
            }
            /* <i18n case="Shown when the mime type of user-provided file does not match any mime types supplied to the rule."> */
            return `${sentence(name)} must be of the type: ${args[0]}`;
            /* </i18n> */
        },
        /**
         * Does not fulfill minimum allowed value
         * @see {@link https://formkit.com/essentials/validation#min}
         */
        min({ name, node: { value }, args }) {
            if (Array.isArray(value)) {
                /* <i18n case="Shown when the length of the array of user-provided values is shorter than the min supplied to the rule."> */
                return `Cannot have fewer than ${args[0]} ${name}.`;
                /* </i18n> */
            }
            /* <i18n case="Shown when the user-provided value is less than the minimum number supplied to the rule."> */
            return `Must be at least ${args[0]} ${name} .`;
            /* </i18n> */
        },
        /**
         * Is not an allowed value
         * @see {@link https://formkit.com/essentials/validation#not}
         */
        not({ name, node: { value } }) {
            /* <i18n case="Shown when the user-provided value matches one of the values supplied to (and thus disallowed by) the rule."> */
            return `“${value}” is not an allowed ${name}.`;
            /* </i18n> */
        },
        /**
         *  Is not a number
         * @see {@link https://formkit.com/essentials/validation#number}
         */
        number({ name }) {
            /* <i18n case="Shown when the user-provided value is not a number."> */
            return `${sentence(name)} must be a number.`;
            /* </i18n> */
        },
        /**
         * Required field.
         * @see {@link https://formkit.com/essentials/validation#required}
         */
        required({ name }) {
            /* <i18n case="Shown when a user does not provide a value to a required input."> */
            return `${sentence(name)} is required.`;
            /* </i18n> */
        },
        /**
         * Does not start with specified value
         * @see {@link https://formkit.com/essentials/validation#starts-with}
         */
        starts_with({ name, args }) {
            /* <i18n case="Shown when the user-provided value does not start with the substring supplied to the rule."> */
            return `${sentence(name)} doesn’t start with ${list(args)}.`;
            /* </i18n> */
        },
        /**
         * Is not a url
         * @see {@link https://formkit.com/essentials/validation#url}
         */
        url() {
            /* <i18n case="Shown when the user-provided value is not a valid url."> */
            return `Please enter a valid URL.`;
            /* </i18n> */
        },
    };

    var en = /*#__PURE__*/Object.freeze({
        __proto__: null,
        ui: ui$u,
        validation: validation$u
    });

    /**
     * Create a new internationalization plugin for FormKit.
     *
     * @param registry - The registry of {@link @formkit/i18n#FormKitLocaleRegistry | FormKitLocales}.
     *
     * @returns {@link @formkit/core#FormKitPlugin | FormKitPlugin}
     *
     * @public
     */
    function createI18nPlugin(registry) {
        return function i18nPlugin(node) {
            let localeKey = parseLocale(node.config.locale, registry);
            let locale = localeKey ? registry[localeKey] : {};
            /* If the locale prop changes, update the active locale */
            node.on('prop:locale', ({ payload: lang }) => {
                localeKey = parseLocale(lang, registry);
                locale = localeKey ? registry[localeKey] : {};
                // Run through all the messages in the store and update with new locale
                node.store.touch();
            });
            node.on('prop:label', () => node.store.touch());
            node.on('prop:validationLabel', () => node.store.touch());
            /**
             * Hook into the core text or t() hook to perform localization on the
             * output of core functionality.
             */
            node.hook.text((fragment, next) => {
                var _a, _b;
                const key = ((_a = fragment.meta) === null || _a === void 0 ? void 0 : _a.messageKey) || fragment.key;
                if (has(locale, fragment.type) && has(locale[fragment.type], key)) {
                    const t = locale[fragment.type][key];
                    if (typeof t === 'function') {
                        fragment.value = Array.isArray((_b = fragment.meta) === null || _b === void 0 ? void 0 : _b.i18nArgs)
                            ? t(...fragment.meta.i18nArgs) // eslint-disable-line @typescript-eslint/no-non-null-assertion
                            : t(fragment);
                    }
                    else {
                        fragment.value = t;
                    }
                }
                return next(fragment);
            });
        };
    }
    /**
     * Parse ISO 639-1 and 639-2 to a valid locale key.
     *
     * @param locale - An ISO 639-1 and (optionally) ISO 639-2 language tag. For these are valid locale keys:
     * zh
     * zh-CN
     * zh-HK
     * en
     * en-GB
     *
     * @param availableLocales - An array of locales that may be valid.
     *
     * @public
     */
    function parseLocale(locale, availableLocales) {
        if (has(availableLocales, locale)) {
            return locale;
        }
        const [lang] = locale.split('-');
        if (has(availableLocales, lang)) {
            return lang;
        }
        for (const locale in availableLocales) {
            return locale;
        }
        return false;
    }

    /**
     * The document's computed CSS styles
     */
    let documentStyles = undefined;
    let documentThemeLinkTag = null;
    /**
     * Stores the state of theme loading
     */
    let themeDidLoad;
    let themeHasLoaded = false;
    let themeWasRequested = false;
    const themeLoaded = new Promise((res) => {
        themeDidLoad = () => {
            themeHasLoaded = true;
            res();
        };
    });
    /**
     * Check if we are client-side
     */
    const isClient = typeof window !== 'undefined' && typeof fetch !== 'undefined';
    documentStyles = isClient
        ? getComputedStyle(document.documentElement)
        : undefined;
    /**
     * The FormKit icon Registry - a global record of loaded icons.
     * @public
     */
    const iconRegistry = {};
    /**
     * A collection of existing icon requests to avoid duplicate fetching
     */
    const iconRequests = {};
    /**
     * Creates the theme plugin based on a given theme name.
     * @param theme - The name or id of the theme to apply.
     * @param icons - Icons you want to add to the global icon registry.
     * @param iconLoaderUrl - A function that returns a remote url for retrieving an
     * SVG icon by name.
     * @param iconLoader - A function that handles loading an icon when it is not
     * found in the registry.
     * @public
     */
    function createThemePlugin(theme, icons, iconLoaderUrl, iconLoader) {
        if (icons) {
            // add any user-provided icons to the registry
            Object.assign(iconRegistry, icons);
        }
        // if we have a theme declared, request it
        if (isClient &&
            !themeWasRequested &&
            (documentStyles === null || documentStyles === void 0 ? void 0 : documentStyles.getPropertyValue('--formkit-theme'))) {
            // we have the theme loaded locally
            themeDidLoad();
            themeWasRequested = true;
        }
        else if (theme && !themeWasRequested && isClient) {
            // we have the theme name but need to request it remotely
            loadTheme(theme);
        }
        else if (!themeWasRequested && isClient) {
            // we don't have a discoverable theme, so don't wait for it
            themeDidLoad();
        }
        const themePlugin = function themePlugin(node) {
            var _a, _b;
            // register the icon handler, and override with local prop value if it exists
            node.addProps(['iconLoader', 'iconLoaderUrl']);
            node.props.iconHandler = createIconHandler(((_a = node.props) === null || _a === void 0 ? void 0 : _a.iconLoader) ? node.props.iconLoader : iconLoader, ((_b = node.props) === null || _b === void 0 ? void 0 : _b.iconLoaderUrl) ? node.props.iconLoaderUrl : iconLoaderUrl);
            loadIconPropIcons(node, node.props.iconHandler);
            node.on('created', () => {
                var _a;
                // set up the `-icon` click handlers
                if ((_a = node === null || node === void 0 ? void 0 : node.context) === null || _a === void 0 ? void 0 : _a.handlers) {
                    node.context.handlers.iconClick = (sectionKey) => {
                        const clickHandlerProp = `on${sectionKey
                        .charAt(0)
                        .toUpperCase()}${sectionKey.slice(1)}IconClick`;
                        const handlerFunction = node.props[clickHandlerProp];
                        if (handlerFunction && typeof handlerFunction === 'function') {
                            return (e) => {
                                return handlerFunction(node, e);
                            };
                        }
                        return undefined;
                    };
                }
            });
        };
        themePlugin.iconHandler = createIconHandler(iconLoader, iconLoaderUrl);
        return themePlugin;
    }
    /**
     * Loads a FormKit theme
     */
    function loadTheme(theme) {
        if (!theme || !isClient || typeof getComputedStyle !== 'function') {
            // if we're not client-side then bail
            return;
        }
        // since we're client-side, flag that we've requested the theme
        themeWasRequested = true;
        documentThemeLinkTag = document.getElementById('formkit-theme');
        // retrieve document styles on plugin creation when the window object exists
        if (theme &&
            // if we have a window object
            isClient &&
            // we don't have an existing theme OR the theme being set up is different
            ((!(documentStyles === null || documentStyles === void 0 ? void 0 : documentStyles.getPropertyValue('--formkit-theme')) &&
                !documentThemeLinkTag) ||
                ((documentThemeLinkTag === null || documentThemeLinkTag === void 0 ? void 0 : documentThemeLinkTag.getAttribute('data-theme')) &&
                    (documentThemeLinkTag === null || documentThemeLinkTag === void 0 ? void 0 : documentThemeLinkTag.getAttribute('data-theme')) !== theme))) {
            // if for some reason we didn't overwrite the __FKV__ token during publish
            // then use the `latest` tag for CDN fetching. (this applies to local dev as well)
            const formkitVersion = FORMKIT_VERSION.startsWith('__')
                ? 'latest'
                : FORMKIT_VERSION;
            const themeUrl = `https://cdn.jsdelivr.net/npm/@formkit/themes@${formkitVersion}/dist/${theme}/theme.css`;
            const link = document.createElement('link');
            link.type = 'text/css';
            link.rel = 'stylesheet';
            link.id = 'formkit-theme';
            link.setAttribute('data-theme', theme);
            link.onload = () => {
                documentStyles = getComputedStyle(document.documentElement); // grab new variables from theme
                themeDidLoad();
            };
            document.head.appendChild(link);
            link.href = themeUrl;
            // if we had an existing theme being loaded, remove it.
            if (documentThemeLinkTag) {
                documentThemeLinkTag.remove();
            }
        }
    }
    /**
     * Returns a function responsible for loading an icon by name.
     * @param iconLoader - a function for loading an icon when it's not found in the
     * iconRegistry.
     * @param iconLoaderUrl - a function that returns a remote URL for retrieving an
     * SVG icon by name.
     * @public
     */
    function createIconHandler(iconLoader, iconLoaderUrl) {
        return (iconName) => {
            if (typeof iconName === 'boolean') {
                return; // do nothing if we're dealing with a boolean
            }
            // if we're dealing with an inline SVG, just use it as-is
            if (iconName.startsWith('<svg')) {
                return iconName;
            }
            if (typeof iconName !== 'string')
                return; // bail if we got something that wasn't a boolean or string
            // is this a default icon that should only load from a stylesheet?
            const isDefault = iconName.startsWith('default:');
            iconName = isDefault ? iconName.split(':')[1] : iconName;
            // check if we've already loaded the icon before
            const iconWasAlreadyLoaded = iconName in iconRegistry;
            let loadedIcon = undefined;
            if (iconWasAlreadyLoaded) {
                return iconRegistry[iconName];
            }
            else if (!iconRequests[iconName]) {
                loadedIcon = getIconFromStylesheet(iconName);
                loadedIcon =
                    isClient && typeof loadedIcon === 'undefined'
                        ? Promise.resolve(loadedIcon)
                        : loadedIcon;
                if (loadedIcon instanceof Promise) {
                    iconRequests[iconName] = loadedIcon
                        .then((iconValue) => {
                        if (!iconValue && typeof iconName === 'string' && !isDefault) {
                            return (loadedIcon =
                                typeof iconLoader === 'function'
                                    ? iconLoader(iconName)
                                    : getRemoteIcon(iconName, iconLoaderUrl));
                        }
                        return iconValue;
                    })
                        .then((finalIcon) => {
                        if (typeof iconName === 'string') {
                            iconRegistry[isDefault ? `default:${iconName}` : iconName] =
                                finalIcon;
                        }
                        return finalIcon;
                    });
                }
                else if (typeof loadedIcon === 'string') {
                    iconRegistry[isDefault ? `default:${iconName}` : iconName] = loadedIcon;
                    return loadedIcon;
                }
            }
            return iconRequests[iconName];
        };
    }
    function getIconFromStylesheet(iconName) {
        if (!isClient)
            return;
        if (themeHasLoaded) {
            return loadStylesheetIcon(iconName);
        }
        else {
            return themeLoaded.then(() => {
                return loadStylesheetIcon(iconName);
            });
        }
    }
    function loadStylesheetIcon(iconName) {
        const cssVarIcon = documentStyles === null || documentStyles === void 0 ? void 0 : documentStyles.getPropertyValue(`--fk-icon-${iconName}`);
        if (cssVarIcon) {
            // if we have a matching icon in the CSS properties, then decode it
            const icon = atob(cssVarIcon);
            if (icon.startsWith('<svg')) {
                iconRegistry[iconName] = icon;
                return icon;
            }
        }
        return undefined;
    }
    /**
     * Attempts to fetch a remote icon from the FormKit CDN
     * @param iconName - The string name of the icon
     * @public
     */
    function getRemoteIcon(iconName, iconLoaderUrl) {
        const formkitVersion = FORMKIT_VERSION.startsWith('__')
            ? 'latest'
            : FORMKIT_VERSION;
        const fetchUrl = typeof iconLoaderUrl === 'function'
            ? iconLoaderUrl(iconName)
            : `https://cdn.jsdelivr.net/npm/@formkit/icons@${formkitVersion}/dist/icons/${iconName}.svg`;
        if (!isClient)
            return undefined;
        return fetch(`${fetchUrl}`)
            .then(async (r) => {
            const icon = await r.text();
            if (icon.startsWith('<svg')) {
                return icon;
            }
            return undefined;
        })
            .catch((e) => {
            console.error(e);
            return undefined;
        });
    }
    /**
     * Loads icons for the matching `-icon` props on a given node
     */
    function loadIconPropIcons(node, iconHandler) {
        const iconRegex = /^[a-zA-Z-]+(?:-icon|Icon)$/;
        const iconProps = Object.keys(node.props).filter((prop) => {
            return iconRegex.test(prop);
        });
        iconProps.forEach((sectionKey) => {
            return loadPropIcon(node, iconHandler, sectionKey);
        });
    }
    /**
     * Loads an icon from an icon-prop declaration eg. suffix-icon="settings"
     */
    function loadPropIcon(node, iconHandler, sectionKey) {
        const iconName = node.props[sectionKey];
        const loadedIcon = iconHandler(iconName);
        const rawIconProp = `_raw${sectionKey
        .charAt(0)
        .toUpperCase()}${sectionKey.slice(1)}`;
        const clickHandlerProp = `on${sectionKey
        .charAt(0)
        .toUpperCase()}${sectionKey.slice(1)}Click`;
        node.addProps([rawIconProp, clickHandlerProp]);
        // listen for changes to the icon prop
        node.on(`prop:${sectionKey}`, reloadIcon);
        if (loadedIcon instanceof Promise) {
            return loadedIcon.then((svg) => {
                node.props[rawIconProp] = svg;
            });
        }
        else {
            node.props[rawIconProp] = loadedIcon;
        }
        return;
    }
    /**
     * reloads an icon when the prop value changes
     */
    function reloadIcon(event) {
        var _a;
        const node = event.origin;
        const iconName = event.payload;
        const iconHandler = (_a = node === null || node === void 0 ? void 0 : node.props) === null || _a === void 0 ? void 0 : _a.iconHandler;
        const sectionKey = event.name.split(':')[1];
        const rawIconProp = `_raw${sectionKey
        .charAt(0)
        .toUpperCase()}${sectionKey.slice(1)}`;
        if (iconHandler && typeof iconHandler === 'function') {
            const loadedIcon = iconHandler(iconName);
            if (loadedIcon instanceof Promise) {
                return loadedIcon.then((svg) => {
                    node.props[rawIconProp] = svg;
                });
            }
            else {
                node.props[rawIconProp] = loadedIcon;
            }
        }
    }

    /**
     * A plugin that creates Vue-specific context object on each given node.
     *
     * @param node - FormKitNode to create the context on.
     *
     * @public
     */
    const vueBindings = function vueBindings(node) {
        /**
         * Start a validity counter on all blocking messages.
         */
        node.ledger.count('blocking', (m) => m.blocking);
        const isValid = vue.ref(!node.ledger.value('blocking'));
        /**
         * Start an error message counter.
         */
        node.ledger.count('errors', (m) => m.type === 'error');
        const hasErrors = vue.ref(!!node.ledger.value('errors'));
        /**
         * Keep track of the first time a Vue tick cycle has passed.
         */
        let hasTicked = false;
        vue.nextTick(() => {
            hasTicked = true;
        });
        /**
         * All messages with the visibility state set to true.
         */
        const availableMessages = vue.reactive(node.store.reduce((store, message) => {
            if (message.visible) {
                store[message.key] = message;
            }
            return store;
        }, {}));
        /**
         * A flag that determines when validation messages should be displayed.
         */
        const validationVisibility = vue.ref(node.props.validationVisibility || 'blur');
        node.on('prop:validationVisibility', ({ payload }) => {
            validationVisibility.value = payload;
        });
        /**
         * Keep track of if this input has ever shown validation errors.
         */
        const hasShownErrors = vue.ref(validationVisibility.value === 'live');
        /**
         * The current visibility state of validation messages.
         */
        const validationVisible = vue.computed(() => {
            if (context.state.submitted)
                return true;
            if (!hasShownErrors.value && !context.state.settled) {
                return false;
            }
            switch (validationVisibility.value) {
                case 'live':
                    return true;
                case 'blur':
                    return context.state.blurred;
                case 'dirty':
                    return context.state.dirty;
                default:
                    return false;
            }
        });
        /**
         * Determines if the input should be considered "complete".
         */
        const isComplete = vue.computed(() => {
            return hasValidation.value
                ? isValid.value && !hasErrors.value
                : context.state.dirty && !empty(context.value);
        });
        /**
         * If the input has validation rules or not.
         */
        const hasValidation = vue.ref(Array.isArray(node.props.parsedRules) && node.props.parsedRules.length > 0);
        node.on('prop:parsedRules', ({ payload: rules }) => {
            hasValidation.value = Array.isArray(rules) && rules.length > 0;
        });
        /**
         * All messages that are currently on display to an end user. This changes
         * based on the current message type visibility, like errorVisibility.
         */
        const messages = vue.computed(() => {
            const visibleMessages = {};
            for (const key in availableMessages) {
                const message = availableMessages[key];
                if (message.type !== 'validation' || validationVisible.value) {
                    visibleMessages[key] = message;
                }
            }
            return visibleMessages;
        });
        /**
         * UI Messages.
         */
        const ui = vue.reactive(node.store.reduce((messages, message) => {
            if (message.type === 'ui' && message.visible)
                messages[message.key] = message;
            return messages;
        }, {}));
        /**
         * This is the reactive data object that is provided to all schemas and
         * forms. It is a subset of data in the core node object.
         */
        const cachedClasses = vue.reactive({});
        const classes = new Proxy(cachedClasses, {
            get(...args) {
                const [target, property] = args;
                let className = Reflect.get(...args);
                if (!className && typeof property === 'string') {
                    if (!has(target, property) && !property.startsWith('__v')) {
                        const observedNode = createObserver(node);
                        observedNode.watch((node) => {
                            const rootClasses = typeof node.config.rootClasses === 'function'
                                ? node.config.rootClasses(property, node)
                                : {};
                            const globalConfigClasses = node.config.classes
                                ? createClasses(property, node, node.config.classes[property])
                                : {};
                            const classesPropClasses = createClasses(property, node, node.props[`_${property}Class`]);
                            const sectionPropClasses = createClasses(property, node, node.props[`${property}Class`]);
                            className = generateClassList(node, property, rootClasses, globalConfigClasses, classesPropClasses, sectionPropClasses);
                            target[property] = className !== null && className !== void 0 ? className : '';
                        });
                    }
                }
                return className;
            },
        });
        const describedBy = vue.computed(() => {
            const describers = [];
            if (context.help) {
                describers.push(`help-${node.props.id}`);
            }
            for (const key in messages.value) {
                describers.push(`${node.props.id}-${key}`);
            }
            return describers.length ? describers.join(' ') : undefined;
        });
        const value = vue.ref(node.value);
        const _value = vue.ref(node.value);
        const context = vue.reactive({
            _value,
            attrs: node.props.attrs,
            disabled: node.props.disabled,
            describedBy,
            fns: {
                length: (obj) => Object.keys(obj).length,
                number: (value) => Number(value),
                string: (value) => String(value),
                json: (value) => JSON.stringify(value),
                eq,
            },
            handlers: {
                blur: (e) => {
                    node.store.set(createMessage({ key: 'blurred', visible: false, value: true }));
                    if (typeof node.props.attrs.onBlur === 'function') {
                        node.props.attrs.onBlur(e);
                    }
                },
                touch: () => {
                    node.store.set(createMessage({ key: 'dirty', visible: false, value: true }));
                },
                DOMInput: (e) => {
                    node.input(e.target.value);
                    node.emit('dom-input-event', e);
                },
            },
            help: node.props.help,
            id: node.props.id,
            label: node.props.label,
            messages,
            node: vue.markRaw(node),
            options: node.props.options,
            defaultMessagePlacement: true,
            state: {
                blurred: false,
                complete: isComplete,
                dirty: false,
                submitted: false,
                settled: node.isSettled,
                valid: isValid,
                errors: hasErrors,
                rules: hasValidation,
                validationVisible,
            },
            type: node.props.type,
            family: node.props.family,
            ui,
            value,
            classes,
        });
        /**
         * Ensure the context object is properly configured after booting up.
         */
        node.on('created', () => {
            if (!eq(context.value, node.value)) {
                _value.value = node.value;
                value.value = node.value;
                vue.triggerRef(value);
                vue.triggerRef(_value);
            }
            node.props._init = cloneAny(node.value);
        });
        /**
         * Sets the settled state.
         */
        node.on('settled', ({ payload: isSettled }) => {
            context.state.settled = isSettled;
        });
        /**
         * Observes node.props properties explicitly and updates them in the context
         * object.
         * @param observe - Props to observe and register as context data.
         */
        function observeProps(observe) {
            observe.forEach((prop) => {
                prop = camel(prop);
                if (!has(context, prop) && has(node.props, prop)) {
                    context[prop] = node.props[prop];
                }
                node.on(`prop:${prop}`, ({ payload }) => {
                    context[prop] = payload;
                });
            });
        }
        /**
         * We use a node observer to individually observe node props.
         */
        const rootProps = () => {
            const props = [
                'help',
                'label',
                'disabled',
                'options',
                'type',
                'attrs',
                'preserve',
                'preserveErrors',
                'id',
            ];
            const iconPattern = /^[a-zA-Z-]+(?:-icon|Icon)$/;
            const matchingProps = Object.keys(node.props).filter((prop) => {
                return iconPattern.test(prop);
            });
            return props.concat(matchingProps);
        };
        observeProps(rootProps());
        /**
         * Once the input is defined, deal with it.
         * @param definition - Type definition.
         */
        function definedAs(definition) {
            if (definition.props)
                observeProps(definition.props);
        }
        node.props.definition && definedAs(node.props.definition);
        /**
         * When new props are added to the core node as "props" (ie not attrs) then
         * we automatically need to start tracking them here.
         */
        node.on('added-props', ({ payload }) => observeProps(payload));
        /**
         * Watch for input events from core.
         */
        node.on('input', ({ payload }) => {
            if (node.type !== 'input' && !vue.isRef(payload) && !vue.isReactive(payload)) {
                _value.value = shallowClone(payload);
            }
            else {
                _value.value = payload;
                vue.triggerRef(_value);
            }
        });
        /**
         * Watch for input commits from core.
         */
        node.on('commit', ({ payload }) => {
            if (node.type !== 'input' && !vue.isRef(payload) && !vue.isReactive(payload)) {
                value.value = _value.value = shallowClone(payload);
            }
            else {
                value.value = _value.value = payload;
                vue.triggerRef(value);
            }
            node.emit('modelUpdated');
            // The input is dirty after a value has been input by a user
            if (!context.state.dirty &&
                node.isCreated &&
                hasTicked &&
                !eq(value.value, node.props._init))
                context.handlers.touch();
            if (isComplete &&
                node.type === 'input' &&
                hasErrors.value &&
                !undefine(node.props.preserveErrors)) {
                node.store.filter((message) => { var _a; return !(message.type === 'error' && ((_a = message.meta) === null || _a === void 0 ? void 0 : _a.autoClear) === true); });
            }
        });
        /**
         * Update the local state in response to messages.
         * @param message - A formkit message
         */
        const updateState = async (message) => {
            if (message.type === 'ui' &&
                message.visible &&
                !message.meta.showAsMessage) {
                ui[message.key] = message;
            }
            else if (message.visible) {
                availableMessages[message.key] = message;
            }
            else if (message.type === 'state') {
                // await node.settled
                context.state[message.key] = !!message.value;
            }
        };
        /**
         * Listen to message events and modify the local message data values.
         */
        node.on('message-added', (e) => updateState(e.payload));
        node.on('message-updated', (e) => updateState(e.payload));
        node.on('message-removed', ({ payload: message }) => {
            delete ui[message.key];
            delete availableMessages[message.key];
            delete context.state[message.key];
        });
        node.on('settled:blocking', () => {
            isValid.value = true;
        });
        node.on('unsettled:blocking', () => {
            isValid.value = false;
        });
        node.on('settled:errors', () => {
            hasErrors.value = false;
        });
        node.on('unsettled:errors', () => {
            hasErrors.value = true;
        });
        /**
         * Watch the validation visible prop and set the hasShownErrors state.
         */
        vue.watch(validationVisible, (value) => {
            if (value) {
                hasShownErrors.value = true;
            }
        });
        node.context = context;
        // The context is complete
        node.emit('context', node, false);
    };

    let registered = false;
    /**
     * Catalog of the error message codes in FormKit.
     * @public
     */
    const errors = {
        /**
         * FormKit errors:
         */
        100: ({ data: node }) => `Only groups, lists, and forms can have children (${node.name}).`,
        101: ({ data: node }) => `You cannot directly modify the store (${node.name}). See: https://formkit.com/advanced/core#message-store`,
        102: ({ data: [node, property], }) => `You cannot directly assign node.${property} (${node.name})`,
        103: ({ data: [operator] }) => `Schema expressions cannot start with an operator (${operator})`,
        104: ({ data: [operator, expression] }) => `Schema expressions cannot end with an operator (${operator} in "${expression}")`,
        105: ({ data: expression }) => `Invalid schema expression: ${expression}`,
        106: ({ data: name }) => `Cannot submit because (${name}) is not in a form.`,
        107: ({ data: [node, value] }) => `Cannot set ${node.name} to non object value: ${value}`,
        108: ({ data: [node, value] }) => `Cannot set ${node.name} to non array value: ${value}`,
        /**
         * Input specific errors:
         */
        300: ({ data: [node] }) => `Cannot set behavior prop to overscroll (on ${node.name} input) when options prop is a function.`,
        /**
         * FormKit vue errors:
         */
        600: ({ data: node }) => `Unknown input type${typeof node.props.type === 'string' ? ' "' + node.props.type + '"' : ''} ("${node.name}")`,
        601: ({ data: node }) => `Input definition${typeof node.props.type === 'string' ? ' "' + node.props.type + '"' : ''} is missing a schema or component property (${node.name}).`,
    };
    /**
     * Catalog of the warning message codes in FormKit.
     * @public
     */
    const warnings = {
        /**
         * Core warnings:
         */
        150: ({ data: fn }) => `Schema function "${fn}()" is not a valid function.`,
        151: ({ data: id }) => `No form element with id: ${id}`,
        152: ({ data: id }) => `No input element with id: ${id}`,
        /**
         * Input specific warnings:
         */
        350: ({ data: node }) => `Invalid options prop for radio input (${node.name}). See https://formkit.com/inputs/radio`,
        /**
         * Vue warnings:
         */
        650: 'Schema "$get()" must use the id of an input to access.',
        651: ({ data: id }) => `Cannot setErrors() on "${id}" because no such id exists.`,
        652: ({ data: id }) => `Cannot clearErrors() on "${id}" because no such id exists.`,
        /**
         * Deprecation warnings:
         */
        800: ({ data: name }) => `${name} is deprecated.`,
    };
    /**
     * Decodes an error that is being emitted and console logs it.
     * @param error - The error currently being handled
     * @param next - Call additional handlers
     * @returns
     */
    const decodeErrors = (error, next) => {
        if (error.code in errors) {
            const err = errors[error.code];
            error.message = typeof err === 'function' ? err(error) : err;
        }
        return next(error);
    };
    if (!registered)
        errorHandler(decodeErrors);
    /**
     * Decodes an error that is being emitted and console logs it.
     * @param error - The error currently being handled
     * @param next - Call additional handlers
     * @returns
     */
    const decodeWarnings = (warning, next) => {
        if (warning.code in warnings) {
            const warn = warnings[warning.code];
            warning.message = typeof warn === 'function' ? warn(warning) : warn;
        }
        return next(warning);
    };
    if (!registered)
        warningHandler(decodeWarnings);
    registered = true;

    /**
     * Default configuration options. Includes all validation rules,
     * en i18n messages.
     *
     * @public
     */
    const defaultConfig = (options = {}) => {
        const { rules = {}, locales = {}, inputs = {}, messages = {}, locale = undefined, theme = undefined, iconLoaderUrl = undefined, iconLoader = undefined, icons = {}, ...nodeOptions } = options;
        /**
         * The default configuration includes the validation plugin,
         * with all core-available validation rules.
         */
        const validation = createValidationPlugin({
            ...defaultRules,
            ...(rules || {}),
        });
        /**
         * Includes the i18n plugin with only the english language
         * messages.
         */
        const i18n = createI18nPlugin(extend({ en, ...(locales || {}) }, messages));
        /**
         * Create the library of inputs that are generally available. This default
         * config imports all "native" inputs by default, but
         */
        const library = createLibraryPlugin(index, inputs);
        /**
         * Create the theme plugin for the user provided theme
         */
        const themePlugin = createThemePlugin(theme, icons, iconLoaderUrl, iconLoader);
        return extend({
            plugins: [library, themePlugin, vueBindings, i18n, validation],
            ...(!locale ? {} : { config: { locale } }),
        }, nodeOptions || {}, true);
    };

    /**
     * Renders an icon using the current IconLoader set at the root FormKit config
     *
     * @public
     */
    const FormKitIcon = vue.defineComponent({
        name: 'FormKitIcon',
        props: {
            icon: {
                type: String,
                default: ''
            },
            iconLoader: {
                type: Function,
                default: null
            },
            iconLoaderUrl: {
                type: Function,
                default: null
            }
        },
        setup(props) {
            var _a, _b;
            const icon = vue.ref(undefined);
            const config = vue.inject(optionsSymbol, {});
            const parent = vue.inject(parentSymbol, null);
            let iconHandler = undefined;
            function loadIcon() {
                if (!iconHandler || typeof iconHandler !== 'function')
                    return;
                const iconOrPromise = iconHandler(props.icon);
                if (iconOrPromise instanceof Promise) {
                    iconOrPromise.then((iconValue) => {
                        icon.value = iconValue;
                    });
                }
                else {
                    icon.value = iconOrPromise;
                }
            }
            if (props.iconLoader && typeof props.iconLoader === 'function') {
                // if we have a locally supplied loader, then use it
                iconHandler = createIconHandler(props.iconLoader);
            }
            else if (parent && ((_a = parent.props) === null || _a === void 0 ? void 0 : _a.iconLoader)) {
                // otherwise try to inherit from a parent
                iconHandler = createIconHandler(parent.props.iconLoader);
            }
            else if (props.iconLoaderUrl && typeof props.iconLoaderUrl === 'function') {
                iconHandler = createIconHandler(iconHandler, props.iconLoaderUrl);
            }
            else {
                // grab our iconHandler from the global config
                const iconPlugin = (_b = config === null || config === void 0 ? void 0 : config.plugins) === null || _b === void 0 ? void 0 : _b.find(plugin => {
                    return typeof plugin.iconHandler === 'function';
                });
                if (iconPlugin) {
                    iconHandler = iconPlugin.iconHandler;
                }
            }
            vue.watch(() => props.icon, () => {
                loadIcon();
            }, { immediate: true });
            return () => {
                if (props.icon && icon.value) {
                    return vue.h('span', {
                        class: 'formkit-icon',
                        innerHTML: icon.value
                    });
                }
                return null;
            };
        }
    });

    exports.FormKit = FormKit;
    exports.FormKitIcon = FormKitIcon;
    exports.FormKitMessages = FormKitMessages;
    exports.FormKitSchema = FormKitSchema;
    exports.bindings = vueBindings;
    exports.clearErrors = clearErrors;
    exports.configSymbol = configSymbol;
    exports.createInput = createInput;
    exports.defaultConfig = defaultConfig;
    exports.errorHandler = errorHandler;
    exports.optionsSymbol = optionsSymbol;
    exports.parentSymbol = parentSymbol;
    exports.plugin = plugin;
    exports.reset = reset;
    exports.resetCount = resetCount;
    exports.setErrors = setErrors;
    exports.submitForm = submitForm;
    exports.useInput = useInput;
    exports.watchVerbose = watchVerbose;

    Object.defineProperty(exports, '__esModule', { value: true });

    return exports;

})({}, Vue);
