text: import { isReactive, isReadonly, unref, reactive, computed, isRef, watch, ref, nextTick, getCurrentInstance, isVue3, onBeforeMount, onBeforeUnmount, inject, provide } from 'vue-demi';
  
  function unwrapObj(obj, ignoreKeys = []) {
    return Object.keys(obj).reduce((o, k) => {
      if (ignoreKeys.includes(k)) return o;
      o[k] = unref(obj[k]);
      return o;
    }, {});
  }
  function isFunction(val) {
    return typeof val === 'function';
  }
  function isProxy(value) {
    return isReactive(value) || isReadonly(value);
  }
  
  function _call(body, then, direct) {
    if (direct) {
      return then ? then(body()) : body();
    }
  
    try {
      var result = Promise.resolve(body());
      return then ? result.then(then) : result;
    } catch (e) {
      return Promise.reject(e);
    }
  }
  
  let ROOT_PATH = '__root';
  /**
   * @typedef {import('vue-demi').ComponentPublicInstance} VueInstance
   */
  
  /**
   * @typedef {import('vue-demi').ComputedRef} ComputedRef
   */
  
  /**
   * @typedef {import('vue-demi').WatchStopHandle} WatchStopHandle
   */
  
  /**
   * @typedef NormalizedValidator
   * @property {Validator} $validator
   * @property {String | Ref<String> | function(*): string} [$message]
   * @property {Object | Ref<Object>} [$params]
   * @property {Object | Ref<Object>} [$async]
   * @property {Ref<*>[]} [$watchTargets]
   */
  
  /**
   * Response form a raw Validator function.
   * Should return a Boolean or an object with $invalid property.
   * @typedef {Boolean | { $valid: Boolean }} ValidatorResponse
   */
  
  /**
   * Raw validator function, before being normalized
   * Can return a Promise or a {@see ValidatorResponse}
   * @typedef {function(*): ((Promise<ValidatorResponse> | ValidatorResponse))} Validator
   */
  
  /**
   * Sorts the validators for a state tree branch
   * @param {Object<NormalizedValidator|Function>} validationsRaw
   * @return {{ rules: Object<NormalizedValidator>, nestedValidators: Object, config: GlobalConfig }}
   */
  
  function _async(f) {
    return function () {
      for (var args = [], i = 0; i < arguments.length; i++) {
        args[i] = arguments[i];
      }
  
      try {
        return Promise.resolve(f.apply(this, args));
      } catch (e) {
        return Promise.reject(e);
      }
    };
  }
  /**
   * Calls a validation rule by unwrapping its value first from a ref.
   * @param {Validator} rule
   * @param {Ref} value
   * @param {VueInstance} instance
   * @return {Promise<ValidatorResponse> | ValidatorResponse}
   */
  
  
  function sortValidations(validationsRaw = {}) {
    const validations = unref(validationsRaw);
    const validationKeys = Object.keys(validations);
    const rules = {};
    const nestedValidators = {};
    const config = {};
    validationKeys.forEach(key => {
      const v = validations[key];
  
      switch (true) {
        // If it is already normalized, use it
        case isFunction(v.$validator):
          rules[key] = v;
          break;
        // If it is just a function, normalize it first
        // into { $validator: <Fun> }
  
        case isFunction(v):
          rules[key] = {
            $validator: v
          };
          break;
        // Catch $-prefixed properties as config
  
        case key.startsWith('$'):
          config[key] = v;
          break;
        // If it doesn’t match any of the above,
        // treat as nestedValidators state property
  
        default:
          nestedValidators[key] = v;
      }
    });
    return {
      rules,
      nestedValidators,
      config
    };
  }
  
  function callRule(rule, value, instance) {
    const v = unref(value);
    return rule.call(instance, v, instance);
  }
  /**
   * Normalizes the validator result
   * Allows passing a boolean of an object like `{ $valid: Boolean }`
   * @param {ValidatorResponse} result - Validator result
   * @return {boolean}
   */
  
  
  function normalizeValidatorResponse(result) {
    return result.$valid !== undefined ? !result.$valid : !result;
  }
  /**
   * Returns the result of an async validator.
   * @param {Validator} rule
   * @param {Ref<*>} model
   * @param {Ref<Boolean>} $pending
   * @param {Ref<Boolean>} $dirty
   * @param {GlobalConfig} config
   * @param {boolean} config.$lazy
   * @param {Ref<*>} $response
   * @param {VueInstance} instance
   * @param {Ref<*>[]} watchTargets
   * @return {{ $invalid: Ref<Boolean>, $unwatch: WatchStopHandle }}
   */
  
  
  function createAsyncResult(rule, model, $pending, $dirty, {
    $lazy
  }, $response, instance, watchTargets = []) {
    const $invalid = ref(!!$dirty.value);
    const $pendingCounter = ref(0);
    $pending.value = false;
    const $unwatch = watch([model, $dirty].concat(watchTargets), () => {
      if ($lazy && !$dirty.value) return false;
      let ruleResult; // make sure we dont break if a validator throws
  
      try {
        ruleResult = callRule(rule, model, instance);
      } catch (err) {
        // convert to a promise, so we can handle it async
        ruleResult = Promise.reject(err);
      }
  
      $pendingCounter.value++;
      $pending.value = !!$pendingCounter.value; // ensure $invalid is false, while validator is resolving
  
      $invalid.value = false;
      Promise.resolve(ruleResult).then(data => {
        $pendingCounter.value--;
        $pending.value = !!$pendingCounter.value;
        $response.value = data;
        $invalid.value = normalizeValidatorResponse(data);
      }).catch(error => {
        $pendingCounter.value--;
        $pending.value = !!$pendingCounter.value;
        $response.value = error;
        $invalid.value = true;
      });
    }, {
      immediate: true,
      deep: typeof model === 'object'
    });
    return {
      $invalid,
      $unwatch
    };
  }
  /**
   * Returns the result of a sync validator
   * @param {Validator} rule
   * @param {Ref<*>} model
   * @param {Ref<Boolean>} $dirty
   * @param {GlobalConfig} config
   * @param {Boolean} config.$lazy
   * @param {Ref<*>} $response
   * @param {VueInstance} instance
   * @return {{$unwatch: (function(): {}), $invalid: ComputedRef<boolean>}}
   */
  
  
  function createSyncResult(rule, model, $dirty, {
    $lazy
  }, $response, instance) {
    const $unwatch = () => ({});
  
    const $invalid = computed(() => {
      if ($lazy && !$dirty.value) return false;
  
      try {
        const result = callRule(rule, model, instance);
        $response.value = result;
        return normalizeValidatorResponse(result);
      } catch (err) {
        $response.value = err;
      }
  
      return true;
    });
    return {
      $unwatch,
      $invalid
    };
  }
  /**
   * Returns the validation result.
   * Detects async and sync validators.
   * @param {NormalizedValidator} rule
   * @param {Ref<*>} model
   * @param {Ref<boolean>} $dirty
   * @param {GlobalConfig} config - Vuelidate config
   * @param {VueInstance} instance - component instance
   * @param {string} validatorName - name of the current validator
   * @param {string} propertyKey - the current property we are validating
   * @param {string} propertyPath - the deep path to the validated property
   * @return {{ $params: *, $message: Ref<String>, $pending: Ref<Boolean>, $invalid: Ref<Boolean>, $response: Ref<*>, $unwatch: WatchStopHandle }}
   */
  
  
  function createValidatorResult(rule, model, $dirty, config, instance, validatorName, propertyKey, propertyPath) {
    const $pending = ref(false);
    const $params = rule.$params || {};
    const $response = ref(null);
    let $invalid;
    let $unwatch;
  
    if (rule.$async) {
      ({
        $invalid,
        $unwatch
      } = createAsyncResult(rule.$validator, model, $pending, $dirty, config, $response, instance, rule.$watchTargets));
    } else {
      ({
        $invalid,
        $unwatch
      } = createSyncResult(rule.$validator, model, $dirty, config, $response, instance));
    }
  
    const message = rule.$message;
    const $message = isFunction(message) ? computed(() => message(unwrapObj({
      $pending,
      $invalid,
      $params: unwrapObj($params),
      // $params can hold refs, so we unwrap them for easy access
      $model: model,
      $response,
      $validator: validatorName,
      $propertyPath: propertyPath,
      $property: propertyKey
    }))) : message || '';
    return {
      $message,
      $params,
      $pending,
      $invalid,
      $response,
      $unwatch
    };
  }
  /**
   * @typedef ErrorObject
   * @property {Ref<String>} $message - Reactive error message
   * @property {Ref<Object>} $params - Params passed from withParams
   * @property {Ref<Boolean>} $pending - If validation is pending
   * @property {String} $property - State key
   * @property {String} $propertyPath - Dot notation path to state
   * @property {String} $validator - Validator name
   * @property {String} $uid - Unique identifier
   */
  
  /**
   * @typedef ValidationResult
   * @property {Ref<Boolean>} $pending
   * @property {Ref<Boolean>} $dirty
   * @property {Ref<Boolean>} $invalid
   * @property {Ref<Boolean>} $error
   * @property {Ref<String>} $path
   * @property {Function} $touch
   * @property {Function} $reset
   * @property {ComputedRef<ErrorObject[]>} $errors
   * @property {ComputedRef<ErrorObject[]>} $silentErrors
   */
  
  /**
   * Creates the main Validation Results object for a state tree
   * Walks the tree's top level branches
   * @param {Object<NormalizedValidator>} rules - Rules for the current state tree
   * @param {Object} model - Current state value
   * @param {String} key - Key for the current state tree
   * @param {ResultsStorage} [resultsCache] - A cache map of all the validators
   * @param {String} [path] - the current property path
   * @param {GlobalConfig} [config] - the config object
   * @param {VueInstance} instance
   * @param {ComputedRef<Object>} externalResults
   * @return {ValidationResult | {}}
   */
  
  
  function createValidationResults(rules, model, key, resultsCache, path, config, instance, externalResults) {
    // collect the property keys
    const ruleKeys = Object.keys(rules);
    const cachedResult = resultsCache.get(path, rules);
    let $dirty = ref(false);
  
    if (cachedResult) {
      // if the rules are the same as before, use the cached results
      if (!cachedResult.$partial) return cachedResult; // remove old watchers
  
      cachedResult.$unwatch(); // use the `$dirty.value`, so we dont save references by accident
  
      $dirty.value = cachedResult.$dirty.value;
    }
  
    const result = {
      // restore $dirty from cache
      $dirty,
      $path: path,
      $touch: () => {
        if (!$dirty.value) $dirty.value = true;
      },
      $reset: () => {
        if ($dirty.value) $dirty.value = false;
      }
    };
    /**
     * If there are no validation rules, it is most likely
     * a top level state, aka root
     */
  
    if (!ruleKeys.length) {
      // if there are cached results, we should overwrite them with the new ones
      cachedResult && resultsCache.set(path, rules, result);
      return result;
    }
  
    ruleKeys.forEach(ruleKey => {
      result[ruleKey] = createValidatorResult(rules[ruleKey], model, result.$dirty, config, instance, ruleKey, key, path);
    });
    result.$externalResults = computed(() => {
      if (!externalResults.value) return [];
      return [].concat(externalResults.value).map((stringError, index) => ({
        $propertyPath: path,
        $property: key,
        $validator: '$externalResults',
        $uid: `${path}-externalResult-${index}`,
        $message: stringError,
        $params: {},
        $response: null,
        $pending: false
      }));
    });
    result.$invalid = computed(() => !!result.$externalResults.value.length || ruleKeys.some(ruleKey => unref(result[ruleKey].$invalid)));
    result.$pending = computed(() => ruleKeys.some(ruleKey => unref(result[ruleKey].$pending)));
    result.$error = computed(() => result.$dirty.value ? result.$pending.value || result.$invalid.value : false);
    result.$silentErrors = computed(() => ruleKeys.filter(ruleKey => unref(result[ruleKey].$invalid)).map(ruleKey => {
      const res = result[ruleKey];
      return reactive({
        $propertyPath: path,
        $property: key,
        $validator: ruleKey,
        $uid: `${path}-${ruleKey}`,
        $message: res.$message,
        $params: res.$params,
        $response: res.$response,
        $pending: res.$pending
      });
    }).concat(result.$externalResults.value));
    result.$errors = computed(() => result.$dirty.value ? result.$silentErrors.value : []);
  
    result.$unwatch = () => ruleKeys.forEach(ruleKey => {
      result[ruleKey].$unwatch();
    });
  
    resultsCache.set(path, rules, result);
    return result;
  }
  /**
   * Collects the validation results of all nested state properties
   * @param {Object<NormalizedValidator|Function>} validations - The validation
   * @param {Object} nestedState - Current state
   * @param {String} path - Path to current property
   * @param {ResultsStorage} resultsCache - Validations cache map
   * @param {GlobalConfig} config - The config object
   * @param {VueInstance} instance - The current Vue instance
   * @param {ComputedRef<object>} nestedExternalResults - The external results for this nested collection
   * @return {{}}
   */
  
  
  function collectNestedValidationResults(validations, nestedState, path, resultsCache, config, instance, nestedExternalResults) {
    const nestedValidationKeys = Object.keys(validations); // if we have no state, return empty object
  
    if (!nestedValidationKeys.length) return {};
    return nestedValidationKeys.reduce((results, nestedKey) => {
      // build validation results for nested state
      results[nestedKey] = setValidations({
        validations: validations[nestedKey],
        state: nestedState,
        key: nestedKey,
        parentKey: path,
        resultsCache,
        globalConfig: config,
        instance,
        externalResults: nestedExternalResults
      });
      return results;
    }, {});
  }
  /**
   * Generates the Meta fields from the results
   * @param {ValidationResult|{}} results
   * @param {Object.<string, ValidationResult>[]} nestedResults
   * @param {Object.<string, ValidationResult>[]} childResults
   * @return {{$anyDirty: Ref<Boolean>, $error: Ref<Boolean>, $invalid: Ref<Boolean>, $errors: Ref<ErrorObject[]>, $dirty: Ref<Boolean>, $touch: Function, $reset: Function }}
   */
  
  
  function createMetaFields(results, nestedResults, childResults) {
    const allResults = computed(() => [nestedResults, childResults].filter(res => res).reduce((allRes, res) => {
      return allRes.concat(Object.values(unref(res)));
    }, [])); // returns `$dirty` as true, if all children are dirty
  
    const $dirty = computed({
      get() {
        return results.$dirty.value || (allResults.value.length ? allResults.value.every(r => r.$dirty) : false);
      },
  
      set(v) {
        results.$dirty.value = v;
      }
  
    });
    const $silentErrors = computed(() => {
      // current state level errors, fallback to empty array if root
      const modelErrors = unref(results.$silentErrors) || []; // collect all nested and child $silentErrors
  
      const nestedErrors = allResults.value.filter(result => (unref(result).$silentErrors || []).length).reduce((errors, result) => {
        return errors.concat(...result.$silentErrors);
      }, []); // merge the $silentErrors
  
      return modelErrors.concat(nestedErrors);
    });
    const $errors = computed(() => {
      // current state level errors, fallback to empty array if root
      const modelErrors = unref(results.$errors) || []; // collect all nested and child $errors
  
      const nestedErrors = allResults.value.filter(result => (unref(result).$errors || []).length).reduce((errors, result) => {
        return errors.concat(...result.$errors);
      }, []); // merge the $errors
  
      return modelErrors.concat(nestedErrors);
    });
    const $invalid = computed(() => // if any of the nested values is invalid
    allResults.value.some(r => r.$invalid) || // or if the current state is invalid
    unref(results.$invalid) || // fallback to false if is root
    false);
    const $pending = computed(() => // if any of the nested values is pending
    allResults.value.some(r => unref(r.$pending)) || // if any of the current state validators is pending
    unref(results.$pending) || // fallback to false if is root
    false);
    const $anyDirty = computed(() => allResults.value.some(r => r.$dirty) || allResults.value.some(r => r.$anyDirty) || $dirty.value);
    const $error = computed(() => $dirty.value ? $pending.value || $invalid.value : false);
  
    const $touch = () => {
      // call the root $touch
      results.$touch(); // call all nested level $touch
  
      allResults.value.forEach(result => {
        result.$touch();
      });
    };
  
    const $reset = () => {
      // reset the root $dirty state
      results.$reset(); // reset all the children $dirty states
  
      allResults.value.forEach(result => {
        result.$reset();
      });
    }; // Ensure that if all child and nested results are $dirty, this also becomes $dirty
  
  
    if (allResults.value.length && allResults.value.every(nr => nr.$dirty)) $touch();
    return {
      $dirty,
      $errors,
      $invalid,
      $anyDirty,
      $error,
      $pending,
      $touch,
      $reset,
      $silentErrors
    };
  }
  /**
   * @typedef VuelidateState
   * @property {WritableComputedRef<any>} $model
   * @property {ComputedRef<Boolean>} $dirty
   * @property {ComputedRef<Boolean>} $error
   * @property {ComputedRef<ErrorObject[]>} $errors
   * @property {ComputedRef<Boolean>} $invalid
   * @property {ComputedRef<Boolean>} $anyDirty
   * @property {ComputedRef<Boolean>} $pending
   * @property {Function} $touch
   * @property {Function} $reset
   * @property {String} $path
   * @property {ComputedRef<ErrorObject[]>} $silentErrors
   * @property {Function} [$validate]
   * @property {Function} [$getResultsForChild]
   * @property {Object.<string, VuelidateState>}
   */
  
  /**
   * Main Vuelidate bootstrap function.
   * Used both for Composition API in `setup` and for Global App usage.
   * Used to collect validation state, when walking recursively down the state tree
   * @param {Object} params
   * @param {Object<NormalizedValidator|Function>} params.validations
   * @param {Object} params.state
   * @param {String} [params.key] - Current state property key. Used when being called on nested items
   * @param {String} [params.parentKey] - Parent state property key. Used when being called recursively
   * @param {Object<ValidationResult>} [params.childResults] - Used to collect child results.
   * @param {ResultsStorage} params.resultsCache - The cached validation results
   * @param {VueInstance} params.instance - The current Vue instance
   * @param {GlobalConfig} params.globalConfig - The validation config, passed to this setValidations instance.
   * @param {Reactive<object> | Ref<Object>} params.externalResults - External validation results
   * @return {UnwrapNestedRefs<VuelidateState>}
   */
  
  
  function setValidations({
    validations,
    state,
    key,
    parentKey,
    childResults,
    resultsCache,
    globalConfig = {},
    instance,
    externalResults
  }) {
    const path = parentKey ? `${parentKey}.${key}` : key; // Sort out the validation object into:
    // – rules = validators for current state tree fragment
    // — nestedValidators = nested state fragments keys that might contain more validators
    // – config = configuration properties that affect this state fragment
  
    const {
      rules,
      nestedValidators,
      config
    } = sortValidations(validations);
    const mergedConfig = Object.assign({}, globalConfig, config); // create protected state for cases when the state branch does not exist yet.
    // This protects when using the OptionsAPI as the data is bound after the setup method
  
    const nestedState = key ? computed(() => {
      const s = unref(state);
      return s ? unref(s[key]) : undefined;
    }) : state; // cache the external results, so we can revert back to them
  
    const cachedExternalResults = Object.assign({}, unref(externalResults) || {});
    const nestedExternalResults = computed(() => {
      const results = unref(externalResults);
      if (!key) return results;
      return results ? unref(results[key]) : undefined;
    }); // Use rules for the current state fragment and validate it
  
    const results = createValidationResults(rules, nestedState, key, resultsCache, path, mergedConfig, instance, nestedExternalResults); // Use nested keys to repeat the process
    // *WARN*: This is recursive
  
    const nestedResults = collectNestedValidationResults(nestedValidators, nestedState, path, resultsCache, mergedConfig, instance, nestedExternalResults); // Collect and merge this level validation results
    // with all nested validation results
  
    const {
      $dirty,
      $errors,
      $invalid,
      $anyDirty,
      $error,
      $pending,
      $touch,
      $reset,
      $silentErrors
    } = createMetaFields(results, nestedResults, childResults);
    /**
     * If we have no `key`, this is the top level state
     * We dont need `$model` there.
     */
  
    const $model = key ? computed({
      get: () => unref(nestedState),
      set: val => {
        $dirty.value = true;
        const s = unref(state);
        const external = unref(externalResults);
  
        if (external) {
          external[key] = cachedExternalResults[key];
        }
  
        if (isRef(s[key])) {
          s[key].value = val;
        } else {
          s[key] = val;
        }
      }
    }) : null;
  
    if (key && mergedConfig.$autoDirty) {
      watch(nestedState, () => {
        if (!$dirty.value) $touch();
        const external = unref(externalResults);
  
        if (external) {
          external[key] = cachedExternalResults[key];
        }
      }, {
        flush: 'sync'
      });
    }
    /**
     * Executes the validators and returns the result.
     * @return {Promise<boolean>}
     */
  
  
    function $validate() {
      return new Promise(_async(function (resolve) {
        if (!$dirty.value) $touch(); // await the watchers
  
        return _call(nextTick, function () {
          // return whether it is valid or not
          if (!$pending.value) return resolve(!$invalid.value);
          const unwatch = watch($pending, () => {
            resolve(!$invalid.value);
            unwatch();
          });
        });
      }));
    }
    /**
     * Returns a child component's results, based on registration name
     * @param {string} key
     * @return {VuelidateState}
     */
  
  
    function $getResultsForChild(key) {
      return (childResults.value || {})[key];
    }
  
    function $clearExternalResults() {
      if (isRef(externalResults)) {
        externalResults.value = cachedExternalResults;
      } else {
        // if the external results state was empty, we need to delete every property, one by one
        if (Object.keys(cachedExternalResults).length === 0) {
          Object.keys(externalResults).forEach(k => {
            delete externalResults[k];
          });
        } else {
          // state was not empty, so we just assign it back into the current state
          Object.assign(externalResults, cachedExternalResults);
        }
      }
    }
  
    return reactive(Object.assign({}, results, {
      // NOTE: The order here is very important, since we want to override
      // some of the *results* meta fields with the collective version of it
      // that includes the results of nested state validation results
      $model,
      $dirty,
      $error,
      $errors,
      $invalid,
      $anyDirty,
      $pending,
      $touch,
      $reset,
      $path: path || ROOT_PATH,
      $silentErrors
    }, childResults && {
      $getResultsForChild,
      $validate,
      $clearExternalResults
    }, nestedResults));
  }
  
  class ResultsStorage {
    constructor() {
      this.storage = new Map();
    }
    /**
     * Stores a validation result, and its rules by its path
     * @param {String} path
     * @param {Object<NormalizedValidator>} rules
     * @param {ValidationResult} result
     */
  
  
    set(path, rules, result) {
      this.storage.set(path, {
        rules,
        result
      });
    }
    /**
     * Check if the stored `results` for the provided `path` have the same `rules` compared to 'storedRules'
     * @param {String} path
     * @param {Object<NormalizedValidator>} rules
     * @param {Object<NormalizedValidator>} storedRules
     * @return {Boolean}
     */
  
  
    checkRulesValidity(path, rules, storedRules) {
      const storedRulesKeys = Object.keys(storedRules);
      const newRulesKeys = Object.keys(rules);
      if (newRulesKeys.length !== storedRulesKeys.length) return false;
      const hasAllValidators = newRulesKeys.every(ruleKey => storedRulesKeys.includes(ruleKey));
      if (!hasAllValidators) return false;
      return newRulesKeys.every(ruleKey => {
        if (!rules[ruleKey].$params) return true;
        return Object.keys(rules[ruleKey].$params).every(paramKey => {
          // make sure to unwrap before comparing
          return unref(storedRules[ruleKey].$params[paramKey]) === unref(rules[ruleKey].$params[paramKey]);
        });
      });
    }
    /**
     * Returns the matched result if catche is valid
     * @param {String} path
     * @param {Object<NormalizedValidator>} rules
     * @return {{$partial: boolean, $dirty: Ref<boolean>}|undefined|ValidationResult}
     */
  
  
    get(path, rules) {
      const storedRuleResultPair = this.storage.get(path);
      if (!storedRuleResultPair) return undefined;
      const {
        rules: storedRules,
        result
      } = storedRuleResultPair;
      const isValidCache = this.checkRulesValidity(path, rules, storedRules);
      const $unwatch = result.$unwatch ? result.$unwatch : () => ({});
      if (!isValidCache) return {
        $dirty: result.$dirty,
        $partial: true,
        $unwatch
      };
      return result;
    }
  
  }
  
  const VuelidateInjectChildResults = Symbol('vuelidate#injectChiildResults');
  const VuelidateRemoveChildResults = Symbol('vuelidate#removeChiildResults');
  const CollectFlag = {
    COLLECT_ALL: true,
    COLLECT_NONE: false
  };
  /**
   * Create helpers to collect validation state from child components
   * @param {Object} params
   * @param {String | Number} params.$scope - Parent component scope
   * @return {{sendValidationResultsToParent: function, childResults: ComputedRef<Object>, removeValidationResultsFromParent: function}}
   */
  
  function nestedValidations({
    $scope
  }) {
    const childResultsRaw = {};
    const childResultsKeys = ref([]);
    const childResults = computed(() => childResultsKeys.value.reduce((results, key) => {
      results[key] = unref(childResultsRaw[key]);
      return results;
    }, {}));
    /**
     * Allows children to send validation data up to their parent.
     * @param {Object} results - the results
     * @param {Object} args
     * @param {String} args.$registerAs - the $registeredAs key
     * @param {String | Number} args.$scope - the $scope key
     */
  
    function injectChildResultsIntoParent(results, {
      $registerAs: key,
      $scope: childScope,
      $stopPropagation
    }) {
      if ($stopPropagation || $scope === CollectFlag.COLLECT_NONE || childScope === CollectFlag.COLLECT_NONE || $scope !== CollectFlag.COLLECT_ALL && $scope !== childScope) return;
      childResultsRaw[key] = results;
      childResultsKeys.value.push(key);
    }
    /**
     * Allows children to remove the validation data from their parent, before getting destroyed.
     * @param {String} key - the registeredAs key
     */
  
  
    function removeChildResultsFromParent(key) {
      // remove the key
      childResultsKeys.value = childResultsKeys.value.filter(childKey => childKey !== key); // remove the stored data for the key
  
      delete childResultsRaw[key];
    } // inject the `injectChildResultsIntoParent` method, into the current scope
  
  
    const sendValidationResultsToParent = inject(VuelidateInjectChildResults, () => {}); // provide to all of its children the send results to parent function
  
    provide(VuelidateInjectChildResults, injectChildResultsIntoParent);
    const removeValidationResultsFromParent = inject(VuelidateRemoveChildResults, () => {}); // provide to all of its children the remove results  function
  
    provide(VuelidateRemoveChildResults, removeChildResultsFromParent);
    return {
      childResults,
      sendValidationResultsToParent,
      removeValidationResultsFromParent
    };
  }
  /**
   * @typedef GlobalConfig
   * @property {String} [$registerAs] - Config Object
   * @property {String | Number | Symbol} [$scope] - A scope to limit child component registration
   * @property {Boolean} [$stopPropagation] - Tells a Vue component to stop sending its results up to the parent
   * @property {Ref<Object>} [$externalResults] - External error messages, like from server validation.
   */
  
  /**
   * Composition API compatible Vuelidate
   * Use inside the `setup` lifecycle hook
   * @param {Object | GlobalConfig} [validations] - Validations Object or the globalConfig.
   * @param {Object} [state] - State object - required if `validations` is a validation object.
   * @param {GlobalConfig} [globalConfig] - Config Object
   * @return {UnwrapRef<*>}
   */
  
  
  function useVuelidate(validations, state, globalConfig = {}) {
    // if we pass only one argument, its most probably the globalConfig.
    // This use case is so parents can just collect results of child forms.
    if (arguments.length === 1) {
      globalConfig = validations;
      validations = undefined;
      state = undefined;
    }
  
    let {
      $registerAs,
      $scope = CollectFlag.COLLECT_ALL,
      $stopPropagation,
      $externalResults
    } = globalConfig;
    let instance = getCurrentInstance();
    const componentOptions = instance ? isVue3 ? instance.type : instance.proxy.$options : {}; // if there is no registration name, add one.
  
    if (!$registerAs && instance) {
      // NOTE:
      // ._uid // Vue 2.x Composition-API plugin
      // .uid // Vue 3.0
      const uid = instance.uid || instance._uid;
      $registerAs = `_vuelidate_${uid}`;
    }
  
    const validationResults = ref({});
    const resultsCache = new ResultsStorage();
    const {
      childResults,
      sendValidationResultsToParent,
      removeValidationResultsFromParent
    } = instance ? nestedValidations({
      $scope
    }) : {
      childResults: ref({})
    }; // Options API
  
    if (!validations && componentOptions.validations) {
      const rules = componentOptions.validations;
      state = ref({});
      onBeforeMount(() => {
        // Delay binding state to validations defined with the Options API until mounting, when the data
        // has been attached to the component instance. From that point on it will be reactive.
        state.value = instance.proxy; // helper proxy for instance property access. It makes every reference
        // reactive for the validation function
  
        function ComputedProxyFactory(target) {
          return new Proxy(target, {
            get(target, prop, receiver) {
              return typeof target[prop] === 'object' ? ComputedProxyFactory(target[prop]) : computed(() => target[prop]);
            }
  
          });
        }
  
        watch(() => isFunction(rules) ? rules.call(state.value, new ComputedProxyFactory(state.value)) : rules, validations => {
          validationResults.value = setValidations({
            validations,
            state,
            childResults,
            resultsCache,
            globalConfig,
            instance: instance.proxy,
            externalResults: instance.proxy.vuelidateExternalResults
          });
        }, {
          immediate: true
        });
      });
      globalConfig = componentOptions.validationsConfig || {};
    } else {
      const validationsWatchTarget = isRef(validations) || isProxy(validations) ? validations // wrap plain objects in a reactive, so we can track changes if they have computed in them.
      : reactive(validations || {});
      watch(validationsWatchTarget, newValidationRules => {
        validationResults.value = setValidations({
          validations: newValidationRules,
          state,
          childResults,
          resultsCache,
          globalConfig,
          instance: instance ? instance.proxy : {},
          externalResults: $externalResults
        });
      }, {
        immediate: true
      });
    }
  
    if (instance) {
      // send all the data to the parent when the function is invoked inside setup.
      sendValidationResultsToParent(validationResults, {
        $registerAs,
        $scope,
        $stopPropagation
      }); // before this component is destroyed, remove all the data from the parent.
  
      onBeforeUnmount(() => removeValidationResultsFromParent($registerAs));
    } // TODO: Change into reactive + watch
  
  
    return computed(() => {
      return Object.assign({}, unref(validationResults.value), childResults.value);
    });
  }
  
  export default useVuelidate;
  export { CollectFlag, useVuelidate };
  