Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

vue3-form-validation

Package Overview
Dependencies
Maintainers
1
Versions
59
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

vue3-form-validation - npm Package Compare versions

Comparing version 4.1.1 to 5.0.0-beta.1

822

dist/vue3-form-validation.cjs.js

@@ -9,4 +9,20 @@ 'use strict';

constructor(value) {
this.next = null;
this.prev = null;
Object.defineProperty(this, "value", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "next", {
enumerable: true,
configurable: true,
writable: true,
value: null
});
Object.defineProperty(this, "prev", {
enumerable: true,
configurable: true,
writable: true,
value: null
});
this.value = value;

@@ -17,5 +33,20 @@ }

constructor() {
this.head = null;
this.tail = null;
this.count = 0;
Object.defineProperty(this, "head", {
enumerable: true,
configurable: true,
writable: true,
value: null
});
Object.defineProperty(this, "tail", {
enumerable: true,
configurable: true,
writable: true,
value: null
});
Object.defineProperty(this, "count", {
enumerable: true,
configurable: true,
writable: true,
value: 0
});
}

@@ -106,3 +137,3 @@ get first() {

}
valuesForwards() {
nodesForwards() {
let node = this.head;

@@ -112,3 +143,3 @@ return {

for (; node !== null; node = node.next) {
yield node.value;
yield node;
}

@@ -118,3 +149,3 @@ }

}
valuesBackwards() {
nodesBackwards() {
let node = this.tail;

@@ -124,3 +155,3 @@ return {

for (; node !== null; node = node.prev) {
yield node.value;
yield node;
}

@@ -132,13 +163,20 @@ }

const isDefined = (x) => x !== null && typeof x !== 'undefined';
const isRecord = (x) => typeof x === 'object' && x !== null && !Array.isArray(x);
const isArray = (x) => Array.isArray(x);
const isObject = (x) => typeof x === 'object' && x !== null;
const isField = (x) => isRecord(x) ? '$value' in x : false;
const isTransformedField = (x) => isRecord(x)
? '$uid' in x && '$value' in x && '$errors' in x && '$validating' in x
: false;
const isSimpleRule = (rule) => typeof rule === 'function';
function* deepIterator(obj, predicate = () => false) {
const stack = new LinkedList();
stack.addLast([obj, null, '', []]);
stack.addLast({ current: obj, parent: null, parentKey: '', path: [] });
while (stack.count > 0) {
const [current, parent, parentKey, path] = stack.last.value;
const { current, parent, parentKey, path } = stack.last.value;
stack.removeLast();
let pushedItemsOnStack = false;
if (typeof current === 'object' &&
current !== null &&
!vue.isRef(current) &&
!predicate(current)) {
if (isObject(current) && !vue.isRef(current) && !predicate(current)) {
const entries = Object.entries(current);

@@ -148,7 +186,18 @@ pushedItemsOnStack = entries.length > 0;

const [key, value] = entries[i];
stack.addLast([value, current, key, [...path, key]]);
stack.addLast({
current: value,
parent: current,
parentKey: key,
path: [...path, key]
});
}
}
if (typeof parent === 'object' && parent !== null) {
yield [parentKey, parent[parentKey], parent, path, !pushedItemsOnStack];
if (isObject(parent)) {
yield {
key: parentKey,
value: parent[parentKey],
parent,
path,
isLeaf: !pushedItemsOnStack
};
}

@@ -158,22 +207,2 @@ }

const isDefined = (x) => x !== null && typeof x !== 'undefined';
const isObject = (x) => typeof x === 'object' && x !== null && !Array.isArray(x);
const isArray = (x) => Array.isArray(x);
const isField = (x) => isObject(x) ? '$value' in x : false;
const isTransformedField = (x) => isObject(x)
? '$uid' in x && '$value' in x && '$errors' in x && '$validating' in x
: false;
function cleanupForm(form, deletedData) {
if (isTransformedField(deletedData)) {
form.onDelete(deletedData.$uid);
return;
}
for (const [, value] of deepIterator(deletedData, isTransformedField)) {
if (isTransformedField(value)) {
form.onDelete(value.$uid);
}
}
}
function set(obj, keys, value) {

@@ -202,19 +231,113 @@ if (keys.length === 0) {

function deepCopy(toClone) {
if (typeof toClone !== 'object') {
return toClone;
if (isObject(toClone)) {
const copy = isArray(toClone) ? [] : {};
for (const { value, path, isLeaf } of deepIterator(toClone)) {
if (isLeaf) {
set(copy, path, value);
}
}
return copy;
}
const copy = isArray(toClone) ? [] : {};
for (const [, value, , path, isLeaf] of deepIterator(toClone)) {
if (isLeaf) {
set(copy, path, value);
return toClone;
}
const trySet = (map) => ({ success, failure }) => (key, value) => {
const _value = map.get(key);
if (_value) {
failure === null || failure === void 0 ? void 0 : failure(_value);
}
else {
map.set(key, value);
success === null || success === void 0 ? void 0 : success(value);
}
};
const tryGet = (map) => ({ success, failure }) => (key) => {
const value = map.get(key);
if (value) {
success(value);
}
else {
failure === null || failure === void 0 ? void 0 : failure();
}
};
function path(path, obj) {
let value = obj[path[0]];
for (let i = 0; i < path.length; i++) {
const key = path[i];
if (value === null || value === undefined) {
return undefined;
}
if (i > 0) {
value = value[key];
}
}
return copy;
return value;
}
let id = 1;
function uid() {
return id++;
}
class PromiseCancel {
constructor() {
Object.defineProperty(this, "promise", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "resolve", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "reject", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
this.assign();
}
cancelResolve(value) {
this.resolve(value);
this.assign();
}
cancelReject(reason) {
this.reject(reason);
this.assign();
}
race(...promises) {
return Promise.race([this.promise, ...promises]);
}
assign() {
this.promise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
}
}
function cleanupForm(form, deletedFormData) {
if (isTransformedField(deletedFormData)) {
form.onDelete(deletedFormData.$uid);
return;
}
for (const { value } of deepIterator(deletedFormData, isTransformedField)) {
if (isTransformedField(value)) {
form.onDelete(value.$uid);
}
}
}
function getResultFormData(transformedFormData) {
const result = {};
for (const [, value, , path, isLeaf] of deepIterator(transformedFormData, isTransformedField)) {
for (const { value, path, isLeaf } of deepIterator(transformedFormData, isTransformedField)) {
if (isTransformedField(value)) {
if (vue.isReactive(value.$value)) {
// Value is reactive -> value is an object or array
// Make sure to do a deep clone to loose the reactive reference
set(result, path, deepCopy(value.$value));

@@ -228,2 +351,3 @@ }

if (vue.isReactive(value)) {
// Same as above
set(result, path, deepCopy(value));

@@ -246,3 +370,3 @@ }

}
else if (isObject(transformedValue.$value)) {
else if (isRecord(transformedValue.$value)) {
const copy = deepCopy(value);

@@ -256,3 +380,3 @@ Object.assign(transformedValue.$value, copy);

}
if (typeof value === 'object') {
if (isObject(value)) {
resetFields(value, transformedFormData[key]);

@@ -263,18 +387,11 @@ }

let uid = 1;
function useUid() {
return uid++;
}
function registerField(form, name, field) {
var _a;
const uid = useUid();
const formField = form.registerField(uid, name, field.$value, (_a = field.$rules) !== null && _a !== void 0 ? _a : []);
const uid$1 = uid();
const formField = form.registerField(uid$1, name, field.$value, field.$validationBehavior || 'lazier', (_a = field.$rules) !== null && _a !== void 0 ? _a : []);
vue.watch(formField.modelValue, () => {
if (formField.touched) {
form.validate(uid);
}
form.validate(uid$1);
}, { deep: true });
return {
$uid: uid,
$uid: uid$1,
$value: formField.modelValue,

@@ -284,12 +401,10 @@ $errors: formField.errors,

$validating: formField.validating,
async $onBlur() {
if (!formField.touched) {
formField.touched = true;
await form.validate(uid);
}
async $setTouched() {
formField.touched = true;
await form.validate(uid$1, true);
}
};
}
function transformFormData(form, data) {
for (const [key, value, parent] of deepIterator(data)) {
function transformFormData(form, formData) {
for (const { key, value, parent } of deepIterator(formData)) {
if (isField(value)) {

@@ -302,85 +417,169 @@ const transformedField = registerField(form, key, value);

const tryGet = (map) => ({ success, failure }) => (key) => {
const value = map.get(key);
if (value) {
success(value);
}
else {
failure === null || failure === void 0 ? void 0 : failure();
}
const VALIDATION_BEHAVIOR_RESTRICTIVENESS = {
aggresive: 0,
lazy: 1,
lazier: 2
};
const trySet = (map) => ({ success, failure }) => (key, value) => {
const _value = map.get(key);
if (_value) {
failure === null || failure === void 0 ? void 0 : failure(_value);
const compareValidationBehavior = (a, b) => VALIDATION_BEHAVIOR_RESTRICTIVENESS[a] -
VALIDATION_BEHAVIOR_RESTRICTIVENESS[b];
const getMostRestrictiveValidationBehavior = (form, keyedValidators) => {
let mostRestrictiveValidationBehavior = 'aggresive';
for (const { meta } of keyedValidators.values()) {
const validationBehavior = meta.formField.getValidationBehavior(form);
if (validationBehavior &&
compareValidationBehavior(validationBehavior, mostRestrictiveValidationBehavior) > 0) {
mostRestrictiveValidationBehavior = validationBehavior;
}
}
else {
map.set(key, value);
success === null || success === void 0 ? void 0 : success(value);
}
return mostRestrictiveValidationBehavior;
};
function path(path, o) {
let value = o[path[0]];
for (let i = 0; i < path.length; i++) {
const key = path[i];
if (value === null || value === undefined) {
return undefined;
}
if (i > 0) {
value = value[key];
}
}
return value;
}
class PromiseCancel {
constructor() {
this.assign();
}
cancelResolve(value) {
this.resolve(value);
this.assign();
}
cancelReject(reason) {
this.reject(reason);
this.assign();
}
race(...promises) {
return Promise.race([this.promise, ...promises]);
}
assign() {
this.promise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
class FormField {
constructor(name, modelValue, validationBehavior, rules) {
Object.defineProperty(this, "_rules", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
}
}
class FormField {
constructor(name, modelValue, rules) {
this.rulesValidating = vue.ref(0);
this.touched = false;
this.errors = vue.computed(() => this._errors.filter(isDefined));
this.validating = vue.computed(() => this.rulesValidating.value > 0);
this.hasError = vue.computed(() => this.errors.value.length > 0);
Object.defineProperty(this, "_buffers", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "_errors", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "_validationBehavior", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "_rulesValidating", {
enumerable: true,
configurable: true,
writable: true,
value: vue.ref(0)
});
Object.defineProperty(this, "initialModelValue", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "name", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "touched", {
enumerable: true,
configurable: true,
writable: true,
value: false
});
Object.defineProperty(this, "modelValue", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "errors", {
enumerable: true,
configurable: true,
writable: true,
value: vue.computed(() => this._errors.filter(isDefined))
});
Object.defineProperty(this, "validating", {
enumerable: true,
configurable: true,
writable: true,
value: vue.computed(() => this._rulesValidating.value > 0)
});
Object.defineProperty(this, "hasError", {
enumerable: true,
configurable: true,
writable: true,
value: vue.computed(() => this.errors.value.length > 0)
});
this._rules = rules.map(rule => isSimpleRule(rule) ? rule : rule.rule);
this._buffers = rules.map(() => new LinkedList());
this._errors = vue.reactive(rules.map(() => null));
this._validationBehavior = validationBehavior;
this.name = name;
this._errors = vue.reactive(rules.map(() => null));
if (vue.isRef(modelValue) || vue.isReactive(modelValue)) {
this.modelValue = modelValue;
this._initialModelValue = deepCopy(vue.unref(modelValue));
this.initialModelValue = deepCopy(vue.unref(modelValue));
}
else if (isObject(modelValue)) {
else if (isRecord(modelValue)) {
this.modelValue = vue.reactive(modelValue);
this._initialModelValue = deepCopy(this.modelValue);
this.initialModelValue = deepCopy(this.modelValue);
}
else {
this.modelValue = vue.ref(modelValue);
this._initialModelValue = deepCopy(vue.unref(modelValue));
this.initialModelValue = deepCopy(vue.unref(modelValue));
}
}
setError(ruleNumber, error) {
this._errors[ruleNumber] = error;
async validate(ruleNumber, modelValues) {
const rule = this._rules[ruleNumber];
const buffer = this._buffers[ruleNumber];
let error;
if (!rule) {
return;
}
const ruleResult = rule(...modelValues.map(vue.unref));
if (typeof (ruleResult === null || ruleResult === void 0 ? void 0 : ruleResult.then) === 'function') {
this._rulesValidating.value++;
const shouldSetError = buffer.addLast(true);
if (shouldSetError.prev) {
shouldSetError.prev.value = false;
}
try {
error = await ruleResult;
}
catch (err) {
error = err;
}
buffer.remove(shouldSetError);
this._rulesValidating.value--;
if (shouldSetError.value) {
this._setError(ruleNumber, error);
}
}
else {
error = ruleResult;
this._setError(ruleNumber, error);
}
}
getValidationBehavior(form) {
return typeof this._validationBehavior === 'function'
? this._validationBehavior({
submitCount: form.submitCount,
errorMessages: this.errors.value
})
: this._validationBehavior;
}
shouldValidate(form, validationBehavior) {
switch (validationBehavior || this.getValidationBehavior(form)) {
case 'aggresive':
return true;
case 'lazy':
if (this.touched) {
return true;
}
break;
case 'lazier':
if (this.touched && this.hasError.value) {
return true;
}
break;
}
return false;
}
reset(toDefaultValues) {

@@ -391,15 +590,29 @@ this.touched = false;

if (isArray(this.modelValue.value)) {
this.modelValue.value = deepCopy(this._initialModelValue);
this.modelValue.value = deepCopy(this.initialModelValue);
}
else {
this.modelValue.value = this._initialModelValue;
this.modelValue.value = this.initialModelValue;
}
}
else {
const copy = deepCopy(this._initialModelValue);
const copy = deepCopy(this.initialModelValue);
Object.assign(this.modelValue, copy);
}
}
for (const buffer of this._buffers) {
for (const shouldSetError of buffer.nodesForwards()) {
shouldSetError.value = false;
}
}
Object.assign(this._errors, this._errors.map(() => null));
}
_setError(ruleNumber, error) {
if (typeof error === 'string') {
this._errors[ruleNumber] = error;
throw error;
}
else {
this._errors[ruleNumber] = null;
}
}
}

@@ -413,48 +626,94 @@

const isSimpleRule = (rule) => typeof rule === 'function';
class Form {
constructor() {
this.simpleMap = new Map();
this.keyedSetMap = new Map();
this.reactiveFormFieldMap = vue.shallowReactive(new Map());
this.trySetKeyedSet = trySet(this.keyedSetMap);
this.tryGetKeyedSet = tryGet(this.keyedSetMap);
this.tryGetSimple = tryGet(this.simpleMap);
this.formFields = vue.ref(new Set());
this.submitting = vue.ref(false);
this.errors = vue.computed(() => {
const errors = [];
for (const formField of this.reactiveFormFieldMap.values()) {
errors.push(...formField.errors.value);
}
return errors;
Object.defineProperty(this, "_simpleValidators", {
enumerable: true,
configurable: true,
writable: true,
value: new Map()
});
Object.defineProperty(this, "_keyedValidators", {
enumerable: true,
configurable: true,
writable: true,
value: new Map()
});
Object.defineProperty(this, "_reactiveFormFieldMap", {
enumerable: true,
configurable: true,
writable: true,
value: vue.shallowReactive(new Map())
});
Object.defineProperty(this, "tryGetSimpleValidators", {
enumerable: true,
configurable: true,
writable: true,
value: tryGet(this._simpleValidators)
});
Object.defineProperty(this, "trySetKeyedValidators", {
enumerable: true,
configurable: true,
writable: true,
value: trySet(this._keyedValidators)
});
Object.defineProperty(this, "tryGetKeyedValidators", {
enumerable: true,
configurable: true,
writable: true,
value: tryGet(this._keyedValidators)
});
Object.defineProperty(this, "submitCount", {
enumerable: true,
configurable: true,
writable: true,
value: 0
});
Object.defineProperty(this, "submitting", {
enumerable: true,
configurable: true,
writable: true,
value: vue.ref(false)
});
Object.defineProperty(this, "errors", {
enumerable: true,
configurable: true,
writable: true,
value: vue.computed(() => {
const errors = [];
for (const formField of this._reactiveFormFieldMap.values()) {
errors.push(...formField.errors.value);
}
return errors;
})
});
}
registerField(uid, name, modelValue, rules) {
const formField = new FormField(name, modelValue, rules);
const simple = {
formField,
keys: [],
vs: [],
rollbacks: []
registerField(uid, name, modelValue, validationBehavior, rules) {
const formField = new FormField(name, modelValue, validationBehavior, rules);
const simpleValidators = {
validators: [],
meta: {
formField,
keys: [],
rollbacks: []
}
};
rules.forEach((rule, ruleNumber) => {
const validate = Form.validateFactory(formField, rule, ruleNumber);
const validator = Form._validatorFactory(formField, ruleNumber);
if (isSimpleRule(rule)) {
if (typeof validate !== 'undefined') {
simple.vs.push(validate);
}
simpleValidators.validators.push(validator);
}
else {
const { key } = rule;
const keyed = {
formField,
v: validate
const keyedValidator = {
validator,
meta: {
formField
}
};
const rollback = () => {
this.tryGetKeyedSet({
success: keyedSet => {
keyedSet.delete(keyed);
if (keyedSet.size === 0) {
this.keyedSetMap.delete(key);
this.tryGetKeyedValidators({
success: keyedValidators => {
keyedValidators.delete(keyedValidator);
if (keyedValidators.size === 0) {
this._keyedValidators.delete(key);
}

@@ -464,25 +723,35 @@ }

};
simple.keys.push(key);
simple.rollbacks.push(rollback);
this.trySetKeyedSet({
failure: keyedSet => keyedSet.add(keyed)
})(key, new Set([keyed]));
simpleValidators.meta.keys.push(key);
simpleValidators.meta.rollbacks.push(rollback);
this.trySetKeyedValidators({
failure: keyedValidators => keyedValidators.add(keyedValidator)
})(key, new Set([keyedValidator]));
}
});
this.simpleMap.set(uid, simple);
this.reactiveFormFieldMap.set(uid, formField);
this.formFields.value.add(formField);
this._simpleValidators.set(uid, simpleValidators);
this._reactiveFormFieldMap.set(uid, formField);
return formField;
}
validate(uid) {
const simple = this.simpleMap.get(uid);
if (simple && simple.formField.touched) {
return Promise.allSettled([
...simple.vs.map(v => v([simple.formField.modelValue])),
...this.getPromisesForKeys(simple.keys)
]);
validate(uid, force = false) {
const simpleValidators = this._simpleValidators.get(uid);
if (simpleValidators) {
const { validators, meta } = simpleValidators;
const shouldValidate = meta.formField.shouldValidate(this);
if (force) {
return Promise.allSettled([
...validators.map(v => v([meta.formField.modelValue])),
...this._getValidationResultsForKeys(meta.keys)
]);
}
if (shouldValidate) {
return Promise.allSettled([
...validators.map(v => v([meta.formField.modelValue])),
...this._getValidationResultsForKeys(meta.keys)
]);
}
}
}
async validateAll(names) {
const settledResults = await Promise.allSettled(this.getPromisesForNames(names));
this.submitCount++;
const settledResults = await Promise.allSettled(this._getValidationResultsForNames(names));
for (const result of settledResults) {

@@ -495,29 +764,24 @@ if (result.status === 'rejected') {

onDelete(uid) {
this.tryGetSimple({
success: ({ rollbacks, formField }) => {
this.formFields.value.delete(formField);
rollbacks.forEach(r => r());
this.tryGetSimpleValidators({
success: ({ meta }) => {
meta.rollbacks.forEach(r => r());
}
})(uid);
this.simpleMap.delete(uid);
this.reactiveFormFieldMap.delete(uid);
this._simpleValidators.delete(uid);
this._reactiveFormFieldMap.delete(uid);
}
resetFields(toDefaultValues = true) {
for (const { formField } of this.simpleMap.values()) {
formField.reset(toDefaultValues);
resetFields(toDefaultValues) {
for (const { meta } of this._simpleValidators.values()) {
meta.formField.reset(toDefaultValues);
}
}
getPromisesForKeys(keys) {
const promises = [];
_getValidationResultsForKeys(keys) {
const validationResults = [];
for (const key of keys) {
this.tryGetKeyedSet({
success: keyedSet => {
if (this.isEveryFormFieldTouchedWith(key)) {
const values = [...keyedSet.values()];
const modelValues = values.map(({ formField }) => formField.modelValue);
const vs = values
.map(({ v }) => v)
.filter(isDefined)
.map(v => v(modelValues));
promises.push(...vs);
this.tryGetKeyedValidators({
success: keyedValidators => {
if (this._shouldEveryFieldValidate(keyedValidators)) {
const values = [...keyedValidators.values()];
const modelValues = values.map(({ meta }) => meta.formField.modelValue);
validationResults.push(...values.map(({ validator }) => validator(modelValues)));
}

@@ -527,82 +791,40 @@ }

}
return promises;
return validationResults;
}
getPromisesForNames(names) {
const promises = [];
if (typeof names === 'undefined') {
for (const { formField, vs } of this.simpleMap.values()) {
formField.touched = true;
promises.push(...vs.map(v => v([formField.modelValue])));
_getValidationResultsForNames(names) {
const validationResults = [];
if (names === undefined) {
for (const { validators, meta } of this._simpleValidators.values()) {
meta.formField.touched = true;
validationResults.push(...validators.map(v => v([meta.formField.modelValue])));
}
promises.push(...this.getPromisesForKeys(this.keyedSetMap.keys()));
validationResults.push(...this._getValidationResultsForKeys(this._keyedValidators.keys()));
}
else if (names.length > 0) {
const nameSet = new Set(names);
for (const { formField, keys, vs } of this.simpleMap.values()) {
if (nameSet.has(formField.name)) {
formField.touched = true;
promises.push(...vs.map(v => v([formField.modelValue])));
promises.push(...this.getPromisesForKeys(keys));
const uniqueNames = new Set(names);
for (const { validators, meta } of this._simpleValidators.values()) {
meta.formField.touched = true;
if (uniqueNames.has(meta.formField.name)) {
validationResults.push(...validators.map(v => v([meta.formField.modelValue])));
validationResults.push(...this._getValidationResultsForKeys(meta.keys));
}
}
}
return promises;
return validationResults;
}
isEveryFormFieldTouchedWith(key) {
let everyFormFieldIsTouched = true;
this.tryGetKeyedSet({
success: keyedSet => {
for (const { formField } of keyedSet) {
if (!formField.touched) {
everyFormFieldIsTouched = false;
break;
}
}
_shouldEveryFieldValidate(keyedValidators) {
let shouldEveryFieldValidate = true;
const mostRestrictiveValidationBehavior = getMostRestrictiveValidationBehavior(this, keyedValidators);
for (const { meta } of keyedValidators) {
if (!meta.formField.shouldValidate(this, mostRestrictiveValidationBehavior)) {
shouldEveryFieldValidate = false;
}
})(key);
return everyFormFieldIsTouched;
}
return shouldEveryFieldValidate;
}
static validateFactory(formField, rule, ruleNumber) {
const buffer = new LinkedList();
const setError = (formField, ruleNumber, error) => {
if (typeof error === 'string' && formField.touched) {
formField.setError(ruleNumber, error);
throw error;
}
else {
formField.setError(ruleNumber, null);
}
static _validatorFactory(formField, ruleNumber) {
const curriedValidator = (formField, ruleNumber) => modelValues => {
return formField.validate(ruleNumber, modelValues);
};
const validator = (formField, rule, ruleNumber) => async (modelValues) => {
let error;
const ruleResult = rule(...modelValues.map(vue.unref));
if (typeof (ruleResult === null || ruleResult === void 0 ? void 0 : ruleResult.then) === 'function') {
formField.rulesValidating.value++;
const node = buffer.addLast(false);
if (node.prev) {
node.prev.value = true;
}
try {
error = await ruleResult;
}
catch (err) {
error = err;
}
buffer.remove(node);
formField.rulesValidating.value--;
if (!node.value) {
setError(formField, ruleNumber, error);
}
}
else {
error = ruleResult;
setError(formField, ruleNumber, error);
}
};
if (isSimpleRule(rule)) {
return validator(formField, rule, ruleNumber);
}
else if (rule.rule) {
return validator(formField, rule.rule, ruleNumber);
}
return curriedValidator(formField, ruleNumber);
}

@@ -613,3 +835,3 @@ }

*
* @param formData The structure of your Form Data.
* @param formData - The structure of your Form Data.
* @description

@@ -621,3 +843,3 @@ * Vue composition function for Form Validation.

* For better type inference, consider defining the structure
* of your `formData` upfront and pass it as the generic parameter `T`:
* of your `formData` upfront and pass it as the generic parameter `FormData`:
* ```

@@ -640,3 +862,2 @@ * type FormData = {

form: transformedFormData,
formFields: form.formFields,
submitting: form.submitting,

@@ -659,21 +880,22 @@ errors: form.errors,

}
if (formData) {
if (formData === undefined) {
form.resetFields(true);
}
else {
form.resetFields(false);
resetFields(formData, transformedFormData);
}
else {
form.resetFields();
}
},
add(path$1, value) {
const lastKey = path$1[path$1.length - 1];
if (typeof lastKey !== 'undefined') {
if (lastKey !== undefined) {
const box = { [lastKey]: value };
transformFormData(form, box);
const x = path(path$1, transformedFormData);
if (Array.isArray(x)) {
x.push(box[lastKey]);
const transformedField = box[lastKey];
const valueAtPath = path(path$1, transformedFormData);
if (Array.isArray(valueAtPath)) {
valueAtPath.push(transformedField);
}
else {
set(transformedFormData, path$1, box[lastKey]);
set(transformedFormData, path$1, transformedField);
}

@@ -684,15 +906,17 @@ }

const lastKey = path$1.pop();
if (typeof lastKey !== 'undefined' && path$1.length === 0) {
cleanupForm(form, transformedFormData[lastKey]);
delete transformedFormData[lastKey];
}
else if (typeof lastKey !== 'undefined') {
const value = path(path$1, transformedFormData);
if (Array.isArray(value)) {
const deleted = value.splice(+lastKey, 1);
cleanupForm(form, deleted);
if (lastKey !== undefined) {
if (path$1.length === 0) {
cleanupForm(form, transformedFormData[lastKey]);
delete transformedFormData[lastKey];
}
else {
cleanupForm(form, value[lastKey]);
delete value[lastKey];
const valueAtPath = path(path$1, transformedFormData);
if (Array.isArray(valueAtPath)) {
const deletedFormData = valueAtPath.splice(+lastKey, 1);
cleanupForm(form, deletedFormData);
}
else {
cleanupForm(form, valueAtPath[lastKey]);
delete valueAtPath[lastKey];
}
}

@@ -699,0 +923,0 @@ }

@@ -1,25 +0,117 @@

import * as vue from 'vue';
import { ref, reactive, Ref, UnwrapRef, ComputedRef } from 'vue';
import { ComputedRef } from 'vue';
import { reactive } from 'vue';
import { Ref } from 'vue';
import { ref } from 'vue';
import { UnwrapRef } from 'vue';
declare function cleanupForm(form: Form, deletedFormData: any): void;
declare type CurriedValidator = (formField: FormField, ruleNumber: number) => (modelValues: unknown[]) => ValidationResult;
declare function deepCopy<T>(toClone: T): T;
declare type DeepIndex<T, Ks extends readonly Key[], R = unknown> = Ks extends [
infer First,
...infer Rest
] ? First extends keyof T ? Rest extends readonly Key[] ? DeepIndex<T[First], Rest> : R : R : T;
declare function deepIterator(obj: object, predicate?: (value: object) => boolean): Generator<DeepIteratorResult, void>;
declare type DeepIteratorResult = {
key: string;
value: any;
parent: any;
path: string[];
isLeaf: boolean;
};
declare type DeepMaybeRef<T> = T extends Ref ? T | UnwrapRef<T> : T extends Record<string, unknown> ? DeepMaybeRefObject<T> : MaybeRef<T>;
declare type DeepMaybeRefObject<T extends Record<string, unknown>> = {
[K in keyof T]: T[K] extends Ref ? T[K] | UnwrapRef<T[K]> : T[K] extends any[] ? MaybeRef<T[K]> : T[K] extends Record<string, unknown> ? DeepMaybeRefObject<T[K]> : MaybeRef<T[K]>;
};
export declare type Field<TValue> = {
$value: n_domain.DeepMaybeRef<TValue>;
$rules?: n_domain.Rule<TValue extends any[] ? TValue : UnwrapRef<TValue>>[];
$validationBehavior?: n_form.ValidationBehavior;
};
declare type FieldNames<T> = T extends (infer TArray)[] ? FieldNames<TArray> : {
[K in keyof T]-?: T[K] extends {
$value: any;
} | undefined ? K : FieldNames<T[K]>;
}[keyof T];
declare class Form {
private _simpleValidators;
private _keyedValidators;
private _reactiveFormFieldMap;
tryGetSimpleValidators: ({ success, failure }: {
success(value: SimpleValidators): void;
failure?(): void;
}) => (key: number) => void;
trySetKeyedValidators: ({ success, failure }: {
success?(value: KeyedValidators): void;
failure?(value: KeyedValidators): void;
}) => (key: string, value: KeyedValidators) => void;
tryGetKeyedValidators: ({ success, failure }: {
success(value: KeyedValidators): void;
failure?(): void;
}) => (key: string) => void;
submitCount: number;
submitting: Ref<boolean>;
errors: ComputedRef<string[]>;
registerField(uid: number, name: string, modelValue: unknown, validationBehavior: ValidationBehavior, rules: n_domain.Rule[]): FormField;
validate(uid: number, force?: boolean): Promise<PromiseSettledResult<string | void>[]> | undefined;
validateAll(names?: readonly n_domain.Key[]): Promise<void>;
onDelete(uid: number): void;
resetFields(toDefaultValues: boolean): void;
private _getValidationResultsForKeys;
private _getValidationResultsForNames;
private _shouldEveryFieldValidate;
private static _validatorFactory;
}
declare class FormField {
private _rules;
private _buffers;
private _errors;
private _initialModelValue;
private _validationBehavior;
private _rulesValidating;
initialModelValue: unknown;
name: string;
rulesValidating: vue.Ref<number>;
touched: boolean;
modelValue: ReturnType<typeof ref> | ReturnType<typeof reactive>;
touched: boolean;
errors: vue.ComputedRef<string[]>;
validating: vue.ComputedRef<boolean>;
hasError: vue.ComputedRef<boolean>;
constructor(name: string, modelValue: any, rules: Rule[]);
setError(ruleNumber: number, error: string | null): void;
errors: ComputedRef<string[]>;
validating: ComputedRef<boolean>;
hasError: ComputedRef<boolean>;
constructor(name: string, modelValue: any, validationBehavior: ValidationBehavior, rules: n_domain.Rule[]);
validate(ruleNumber: number, modelValues: unknown[]): Promise<void>;
getValidationBehavior(form: Form): void | ValidationBehaviorString;
shouldValidate(form: Form, validationBehavior?: ValidationBehaviorString): boolean;
reset(toDefaultValues: boolean): void;
private _setError;
}
declare type RefUnrefObject<T extends Record<string, unknown>> = {
[K in keyof T]: T[K] extends Ref ? T[K] | UnwrapRef<T[K]> : T[K] extends any[] ? Ref<T[K]> | T[K] : T[K] extends Record<string, unknown> ? RefUnref<T[K]> : Ref<T[K]> | T[K];
};
declare type RefUnref<T> = T extends Ref ? T | UnwrapRef<T> : T extends Record<string, unknown> ? RefUnrefObject<T> : Ref<T> | T;
declare const getMostRestrictiveValidationBehavior: (form: Form, keyedValidators: KeyedValidators) => ValidationBehaviorString;
declare type SimpleRule<T = any> = (value: T) => any;
declare function getResultFormData(transformedFormData: any): any;
declare const isArray: (x: unknown) => x is any[];
declare const isDefined: <T>(x: T | null | undefined) => x is T;
declare const isField: <T>(x: unknown) => x is Field<T>;
declare const isObject: (x: unknown) => x is Record<Key, any>;
declare const isRecord: (x: unknown) => x is Record<Key, any>;
declare const isSimpleRule: (rule: Rule) => rule is SimpleRule<any>;
declare const isTransformedField: <T>(x: unknown) => x is TransformedField<T>;
declare type Key = string | number;
declare type KeyedRule = {

@@ -29,7 +121,121 @@ key: string;

};
declare type KeyedValidator = {
validator: Validator;
meta: {
formField: FormField;
};
};
declare type KeyedValidators = Set<KeyedValidator>;
declare class LinkedList<T> {
private head;
private tail;
count: number;
get first(): LinkedListNode<T> | null;
get last(): LinkedListNode<T> | null;
addFirst(value: T): LinkedListNode<T>;
addLast(value: T): LinkedListNode<T>;
remove(node: LinkedListNode<T>): void;
removeFirst(): void;
removeLast(): void;
nodesForwards(): {
[Symbol.iterator](): Generator<LinkedListNode<T>, void, unknown>;
};
nodesBackwards(): {
[Symbol.iterator](): Generator<LinkedListNode<T>, void, unknown>;
};
}
declare class LinkedListNode<T> {
value: T;
next: LinkedListNode<T> | null;
prev: LinkedListNode<T> | null;
constructor(value: T);
}
declare type MaybeRef<T> = Ref<T> | T;
declare namespace n_domain {
export {
deepCopy,
deepIterator,
tryGet,
trySet,
path,
set,
uid,
isArray,
isTransformedField,
isDefined,
isField,
isObject,
isRecord,
isSimpleRule,
Rule,
SimpleRule,
KeyedRule,
LinkedList,
PromiseCancel,
Key,
DeepMaybeRef,
MaybeRef,
DeepIndex,
Tuple
}
}
declare namespace n_form {
export {
cleanupForm,
getResultFormData,
resetFields,
transformFormData,
ValidationBehavior,
ValidationBehaviorInfo,
ValidationBehaviorString,
getMostRestrictiveValidationBehavior,
Form,
FormField,
ValidationError
}
}
declare function path(path: readonly Key[], obj: Record<Key, unknown>): any;
declare class PromiseCancel<T = unknown> {
private promise;
private resolve;
private reject;
constructor();
cancelResolve(value: T | PromiseLike<T>): void;
cancelReject(reason?: any): void;
race<Ps extends readonly Promise<any>[]>(...promises: [...Ps]): Promise<T | ([...Ps][number] extends PromiseLike<infer U> ? U : [...Ps][number])>;
private assign;
}
declare function resetFields(formData: any, transformedFormData: any): void;
declare type ResultFormData<FormData extends object> = FormData extends any ? {
[K in keyof FormData]: FormData[K] extends {
$value: infer TValue;
} | undefined ? UnwrapRef<TValue> : FormData[K] extends object ? ResultFormData<FormData[K]> : FormData[K];
} : never;
declare type Rule<T = any> = SimpleRule<T> | KeyedRule;
declare type Field<TValue> = {
$value: RefUnref<TValue>;
$rules?: Rule<TValue extends any[] ? TValue : UnwrapRef<TValue>>[];
declare function set(obj: any, keys: readonly Key[], value: any): void;
declare type SimpleRule<T = any> = (value: T) => any;
declare type SimpleValidators = {
validators: Validator[];
meta: {
formField: FormField;
keys: string[];
rollbacks: (() => void)[];
};
};
declare type TransformedField<T> = {

@@ -41,31 +247,42 @@ $uid: number;

$validating: boolean;
$onBlur(): void;
$setTouched(): Promise<void>;
};
declare type TransformedFormData<T extends object> = T extends any ? {
[K in keyof T]: T[K] extends Field<infer TValue> | undefined ? T[K] extends undefined ? undefined : TransformedField<UnwrapRef<TValue>> : T[K] extends object ? TransformedFormData<T[K]> : T[K];
declare type TransformedFormData<FormData extends object> = FormData extends any ? {
[K in keyof FormData]: FormData[K] extends {
$value: infer TValue;
} | undefined ? FormData[K] extends undefined ? undefined : TransformedField<UnwrapRef<TValue>> : FormData[K] extends object ? TransformedFormData<FormData[K]> : FormData[K];
} : never;
declare type FormData<T extends object> = T extends any ? {
[K in keyof T]: T[K] extends Field<infer TValue> | undefined ? UnwrapRef<TValue> : T[K] extends object ? FormData<T[K]> : T[K];
} : never;
declare type FieldNames<T> = T extends (infer TArray)[] ? FieldNames<TArray> : {
[K in keyof T]-?: T[K] extends Field<any> | undefined ? K : FieldNames<T[K]>;
}[keyof T];
declare type Keys = readonly (string | number)[];
declare type DeepIndex<T, Ks extends Keys, R = unknown> = Ks extends [
infer First,
...infer Rest
] ? First extends keyof T ? Rest extends Keys ? DeepIndex<T[First], Rest> : R : R : T;
declare type UseValidation<T extends object> = {
form: TransformedFormData<T>;
formFields: Ref<Set<FormField>>;
declare function transformFormData(form: Form, formData: object): void;
declare const tryGet: <K, V>(map: Map<K, V>) => ({ success, failure }: {
success(value: V): void;
failure?(): void;
}) => (key: K) => void;
declare const trySet: <K, V>(map: Map<K, V>) => ({ success, failure }: {
success?(value: V): void;
failure?(value: V): void;
}) => (key: K, value: V) => void;
declare type Tuple<T, N extends number> = number extends N ? T[] : _Tuple<T, N, []>;
declare type _Tuple<T, N extends number, R extends unknown[]> = R['length'] extends N ? R : _Tuple<T, N, [T, ...R]>;
declare function uid(): number;
declare type UseValidation<FormData extends object> = {
form: TransformedFormData<FormData>;
submitting: Ref<boolean>;
errors: ComputedRef<string[]>;
validateFields(names?: FieldNames<T>[] | string[]): Promise<FormData<T>>;
resetFields(formData?: Partial<FormData<T>>): void;
add<Ks extends Keys>(path: readonly [...Ks], value: DeepIndex<T, Ks> extends Array<infer TArray> ? TArray : DeepIndex<T, Ks>): void;
remove(path: (string | number)[]): void;
validateFields(names?: FieldNames<FormData>[] | string[]): Promise<ResultFormData<FormData>>;
resetFields(formData?: Partial<ResultFormData<FormData>>): void;
add<Ks extends readonly n_domain.Key[]>(path: readonly [...Ks], value: n_domain.DeepIndex<FormData, Ks> extends (infer TArray)[] ? TArray : n_domain.DeepIndex<FormData, Ks>): void;
remove(path: n_domain.Key[]): void;
};
/**
*
* @param formData The structure of your Form Data.
* @param formData - The structure of your Form Data.
* @description

@@ -77,3 +294,3 @@ * Vue composition function for Form Validation.

* For better type inference, consider defining the structure
* of your `formData` upfront and pass it as the generic parameter `T`:
* of your `formData` upfront and pass it as the generic parameter `FormData`:
* ```

@@ -89,8 +306,21 @@ * type FormData = {

*/
declare function useValidation<T extends object>(formData: T): UseValidation<T>;
export declare function useValidation<FormData extends object>(formData: FormData): UseValidation<FormData>;
declare class ValidationError extends Error {
declare type ValidationBehavior = ValidationBehaviorString | ((info: ValidationBehaviorInfo) => ValidationBehaviorString | void);
declare type ValidationBehaviorInfo = {
submitCount: number;
errorMessages: string[];
};
declare type ValidationBehaviorString = 'aggresive' | 'lazy' | 'lazier';
export declare class ValidationError extends Error {
constructor();
}
export { Field, ValidationError, useValidation };
declare type ValidationResult = Promise<void | string>;
declare type Validator = ReturnType<CurriedValidator>;
export { }

@@ -5,4 +5,20 @@ import { isRef, isReactive, watch, ref, computed, reactive, unref, shallowReactive } from 'vue';

constructor(value) {
this.next = null;
this.prev = null;
Object.defineProperty(this, "value", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "next", {
enumerable: true,
configurable: true,
writable: true,
value: null
});
Object.defineProperty(this, "prev", {
enumerable: true,
configurable: true,
writable: true,
value: null
});
this.value = value;

@@ -13,5 +29,20 @@ }

constructor() {
this.head = null;
this.tail = null;
this.count = 0;
Object.defineProperty(this, "head", {
enumerable: true,
configurable: true,
writable: true,
value: null
});
Object.defineProperty(this, "tail", {
enumerable: true,
configurable: true,
writable: true,
value: null
});
Object.defineProperty(this, "count", {
enumerable: true,
configurable: true,
writable: true,
value: 0
});
}

@@ -102,3 +133,3 @@ get first() {

}
valuesForwards() {
nodesForwards() {
let node = this.head;

@@ -108,3 +139,3 @@ return {

for (; node !== null; node = node.next) {
yield node.value;
yield node;
}

@@ -114,3 +145,3 @@ }

}
valuesBackwards() {
nodesBackwards() {
let node = this.tail;

@@ -120,3 +151,3 @@ return {

for (; node !== null; node = node.prev) {
yield node.value;
yield node;
}

@@ -128,13 +159,20 @@ }

const isDefined = (x) => x !== null && typeof x !== 'undefined';
const isRecord = (x) => typeof x === 'object' && x !== null && !Array.isArray(x);
const isArray = (x) => Array.isArray(x);
const isObject = (x) => typeof x === 'object' && x !== null;
const isField = (x) => isRecord(x) ? '$value' in x : false;
const isTransformedField = (x) => isRecord(x)
? '$uid' in x && '$value' in x && '$errors' in x && '$validating' in x
: false;
const isSimpleRule = (rule) => typeof rule === 'function';
function* deepIterator(obj, predicate = () => false) {
const stack = new LinkedList();
stack.addLast([obj, null, '', []]);
stack.addLast({ current: obj, parent: null, parentKey: '', path: [] });
while (stack.count > 0) {
const [current, parent, parentKey, path] = stack.last.value;
const { current, parent, parentKey, path } = stack.last.value;
stack.removeLast();
let pushedItemsOnStack = false;
if (typeof current === 'object' &&
current !== null &&
!isRef(current) &&
!predicate(current)) {
if (isObject(current) && !isRef(current) && !predicate(current)) {
const entries = Object.entries(current);

@@ -144,7 +182,18 @@ pushedItemsOnStack = entries.length > 0;

const [key, value] = entries[i];
stack.addLast([value, current, key, [...path, key]]);
stack.addLast({
current: value,
parent: current,
parentKey: key,
path: [...path, key]
});
}
}
if (typeof parent === 'object' && parent !== null) {
yield [parentKey, parent[parentKey], parent, path, !pushedItemsOnStack];
if (isObject(parent)) {
yield {
key: parentKey,
value: parent[parentKey],
parent,
path,
isLeaf: !pushedItemsOnStack
};
}

@@ -154,22 +203,2 @@ }

const isDefined = (x) => x !== null && typeof x !== 'undefined';
const isObject = (x) => typeof x === 'object' && x !== null && !Array.isArray(x);
const isArray = (x) => Array.isArray(x);
const isField = (x) => isObject(x) ? '$value' in x : false;
const isTransformedField = (x) => isObject(x)
? '$uid' in x && '$value' in x && '$errors' in x && '$validating' in x
: false;
function cleanupForm(form, deletedData) {
if (isTransformedField(deletedData)) {
form.onDelete(deletedData.$uid);
return;
}
for (const [, value] of deepIterator(deletedData, isTransformedField)) {
if (isTransformedField(value)) {
form.onDelete(value.$uid);
}
}
}
function set(obj, keys, value) {

@@ -198,19 +227,113 @@ if (keys.length === 0) {

function deepCopy(toClone) {
if (typeof toClone !== 'object') {
return toClone;
if (isObject(toClone)) {
const copy = isArray(toClone) ? [] : {};
for (const { value, path, isLeaf } of deepIterator(toClone)) {
if (isLeaf) {
set(copy, path, value);
}
}
return copy;
}
const copy = isArray(toClone) ? [] : {};
for (const [, value, , path, isLeaf] of deepIterator(toClone)) {
if (isLeaf) {
set(copy, path, value);
return toClone;
}
const trySet = (map) => ({ success, failure }) => (key, value) => {
const _value = map.get(key);
if (_value) {
failure === null || failure === void 0 ? void 0 : failure(_value);
}
else {
map.set(key, value);
success === null || success === void 0 ? void 0 : success(value);
}
};
const tryGet = (map) => ({ success, failure }) => (key) => {
const value = map.get(key);
if (value) {
success(value);
}
else {
failure === null || failure === void 0 ? void 0 : failure();
}
};
function path(path, obj) {
let value = obj[path[0]];
for (let i = 0; i < path.length; i++) {
const key = path[i];
if (value === null || value === undefined) {
return undefined;
}
if (i > 0) {
value = value[key];
}
}
return copy;
return value;
}
let id = 1;
function uid() {
return id++;
}
class PromiseCancel {
constructor() {
Object.defineProperty(this, "promise", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "resolve", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "reject", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
this.assign();
}
cancelResolve(value) {
this.resolve(value);
this.assign();
}
cancelReject(reason) {
this.reject(reason);
this.assign();
}
race(...promises) {
return Promise.race([this.promise, ...promises]);
}
assign() {
this.promise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
}
}
function cleanupForm(form, deletedFormData) {
if (isTransformedField(deletedFormData)) {
form.onDelete(deletedFormData.$uid);
return;
}
for (const { value } of deepIterator(deletedFormData, isTransformedField)) {
if (isTransformedField(value)) {
form.onDelete(value.$uid);
}
}
}
function getResultFormData(transformedFormData) {
const result = {};
for (const [, value, , path, isLeaf] of deepIterator(transformedFormData, isTransformedField)) {
for (const { value, path, isLeaf } of deepIterator(transformedFormData, isTransformedField)) {
if (isTransformedField(value)) {
if (isReactive(value.$value)) {
// Value is reactive -> value is an object or array
// Make sure to do a deep clone to loose the reactive reference
set(result, path, deepCopy(value.$value));

@@ -224,2 +347,3 @@ }

if (isReactive(value)) {
// Same as above
set(result, path, deepCopy(value));

@@ -242,3 +366,3 @@ }

}
else if (isObject(transformedValue.$value)) {
else if (isRecord(transformedValue.$value)) {
const copy = deepCopy(value);

@@ -252,3 +376,3 @@ Object.assign(transformedValue.$value, copy);

}
if (typeof value === 'object') {
if (isObject(value)) {
resetFields(value, transformedFormData[key]);

@@ -259,18 +383,11 @@ }

let uid = 1;
function useUid() {
return uid++;
}
function registerField(form, name, field) {
var _a;
const uid = useUid();
const formField = form.registerField(uid, name, field.$value, (_a = field.$rules) !== null && _a !== void 0 ? _a : []);
const uid$1 = uid();
const formField = form.registerField(uid$1, name, field.$value, field.$validationBehavior || 'lazier', (_a = field.$rules) !== null && _a !== void 0 ? _a : []);
watch(formField.modelValue, () => {
if (formField.touched) {
form.validate(uid);
}
form.validate(uid$1);
}, { deep: true });
return {
$uid: uid,
$uid: uid$1,
$value: formField.modelValue,

@@ -280,12 +397,10 @@ $errors: formField.errors,

$validating: formField.validating,
async $onBlur() {
if (!formField.touched) {
formField.touched = true;
await form.validate(uid);
}
async $setTouched() {
formField.touched = true;
await form.validate(uid$1, true);
}
};
}
function transformFormData(form, data) {
for (const [key, value, parent] of deepIterator(data)) {
function transformFormData(form, formData) {
for (const { key, value, parent } of deepIterator(formData)) {
if (isField(value)) {

@@ -298,85 +413,169 @@ const transformedField = registerField(form, key, value);

const tryGet = (map) => ({ success, failure }) => (key) => {
const value = map.get(key);
if (value) {
success(value);
}
else {
failure === null || failure === void 0 ? void 0 : failure();
}
const VALIDATION_BEHAVIOR_RESTRICTIVENESS = {
aggresive: 0,
lazy: 1,
lazier: 2
};
const trySet = (map) => ({ success, failure }) => (key, value) => {
const _value = map.get(key);
if (_value) {
failure === null || failure === void 0 ? void 0 : failure(_value);
const compareValidationBehavior = (a, b) => VALIDATION_BEHAVIOR_RESTRICTIVENESS[a] -
VALIDATION_BEHAVIOR_RESTRICTIVENESS[b];
const getMostRestrictiveValidationBehavior = (form, keyedValidators) => {
let mostRestrictiveValidationBehavior = 'aggresive';
for (const { meta } of keyedValidators.values()) {
const validationBehavior = meta.formField.getValidationBehavior(form);
if (validationBehavior &&
compareValidationBehavior(validationBehavior, mostRestrictiveValidationBehavior) > 0) {
mostRestrictiveValidationBehavior = validationBehavior;
}
}
else {
map.set(key, value);
success === null || success === void 0 ? void 0 : success(value);
}
return mostRestrictiveValidationBehavior;
};
function path(path, o) {
let value = o[path[0]];
for (let i = 0; i < path.length; i++) {
const key = path[i];
if (value === null || value === undefined) {
return undefined;
}
if (i > 0) {
value = value[key];
}
}
return value;
}
class PromiseCancel {
constructor() {
this.assign();
}
cancelResolve(value) {
this.resolve(value);
this.assign();
}
cancelReject(reason) {
this.reject(reason);
this.assign();
}
race(...promises) {
return Promise.race([this.promise, ...promises]);
}
assign() {
this.promise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
class FormField {
constructor(name, modelValue, validationBehavior, rules) {
Object.defineProperty(this, "_rules", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
}
}
class FormField {
constructor(name, modelValue, rules) {
this.rulesValidating = ref(0);
this.touched = false;
this.errors = computed(() => this._errors.filter(isDefined));
this.validating = computed(() => this.rulesValidating.value > 0);
this.hasError = computed(() => this.errors.value.length > 0);
Object.defineProperty(this, "_buffers", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "_errors", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "_validationBehavior", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "_rulesValidating", {
enumerable: true,
configurable: true,
writable: true,
value: ref(0)
});
Object.defineProperty(this, "initialModelValue", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "name", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "touched", {
enumerable: true,
configurable: true,
writable: true,
value: false
});
Object.defineProperty(this, "modelValue", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "errors", {
enumerable: true,
configurable: true,
writable: true,
value: computed(() => this._errors.filter(isDefined))
});
Object.defineProperty(this, "validating", {
enumerable: true,
configurable: true,
writable: true,
value: computed(() => this._rulesValidating.value > 0)
});
Object.defineProperty(this, "hasError", {
enumerable: true,
configurable: true,
writable: true,
value: computed(() => this.errors.value.length > 0)
});
this._rules = rules.map(rule => isSimpleRule(rule) ? rule : rule.rule);
this._buffers = rules.map(() => new LinkedList());
this._errors = reactive(rules.map(() => null));
this._validationBehavior = validationBehavior;
this.name = name;
this._errors = reactive(rules.map(() => null));
if (isRef(modelValue) || isReactive(modelValue)) {
this.modelValue = modelValue;
this._initialModelValue = deepCopy(unref(modelValue));
this.initialModelValue = deepCopy(unref(modelValue));
}
else if (isObject(modelValue)) {
else if (isRecord(modelValue)) {
this.modelValue = reactive(modelValue);
this._initialModelValue = deepCopy(this.modelValue);
this.initialModelValue = deepCopy(this.modelValue);
}
else {
this.modelValue = ref(modelValue);
this._initialModelValue = deepCopy(unref(modelValue));
this.initialModelValue = deepCopy(unref(modelValue));
}
}
setError(ruleNumber, error) {
this._errors[ruleNumber] = error;
async validate(ruleNumber, modelValues) {
const rule = this._rules[ruleNumber];
const buffer = this._buffers[ruleNumber];
let error;
if (!rule) {
return;
}
const ruleResult = rule(...modelValues.map(unref));
if (typeof (ruleResult === null || ruleResult === void 0 ? void 0 : ruleResult.then) === 'function') {
this._rulesValidating.value++;
const shouldSetError = buffer.addLast(true);
if (shouldSetError.prev) {
shouldSetError.prev.value = false;
}
try {
error = await ruleResult;
}
catch (err) {
error = err;
}
buffer.remove(shouldSetError);
this._rulesValidating.value--;
if (shouldSetError.value) {
this._setError(ruleNumber, error);
}
}
else {
error = ruleResult;
this._setError(ruleNumber, error);
}
}
getValidationBehavior(form) {
return typeof this._validationBehavior === 'function'
? this._validationBehavior({
submitCount: form.submitCount,
errorMessages: this.errors.value
})
: this._validationBehavior;
}
shouldValidate(form, validationBehavior) {
switch (validationBehavior || this.getValidationBehavior(form)) {
case 'aggresive':
return true;
case 'lazy':
if (this.touched) {
return true;
}
break;
case 'lazier':
if (this.touched && this.hasError.value) {
return true;
}
break;
}
return false;
}
reset(toDefaultValues) {

@@ -387,15 +586,29 @@ this.touched = false;

if (isArray(this.modelValue.value)) {
this.modelValue.value = deepCopy(this._initialModelValue);
this.modelValue.value = deepCopy(this.initialModelValue);
}
else {
this.modelValue.value = this._initialModelValue;
this.modelValue.value = this.initialModelValue;
}
}
else {
const copy = deepCopy(this._initialModelValue);
const copy = deepCopy(this.initialModelValue);
Object.assign(this.modelValue, copy);
}
}
for (const buffer of this._buffers) {
for (const shouldSetError of buffer.nodesForwards()) {
shouldSetError.value = false;
}
}
Object.assign(this._errors, this._errors.map(() => null));
}
_setError(ruleNumber, error) {
if (typeof error === 'string') {
this._errors[ruleNumber] = error;
throw error;
}
else {
this._errors[ruleNumber] = null;
}
}
}

@@ -409,48 +622,94 @@

const isSimpleRule = (rule) => typeof rule === 'function';
class Form {
constructor() {
this.simpleMap = new Map();
this.keyedSetMap = new Map();
this.reactiveFormFieldMap = shallowReactive(new Map());
this.trySetKeyedSet = trySet(this.keyedSetMap);
this.tryGetKeyedSet = tryGet(this.keyedSetMap);
this.tryGetSimple = tryGet(this.simpleMap);
this.formFields = ref(new Set());
this.submitting = ref(false);
this.errors = computed(() => {
const errors = [];
for (const formField of this.reactiveFormFieldMap.values()) {
errors.push(...formField.errors.value);
}
return errors;
Object.defineProperty(this, "_simpleValidators", {
enumerable: true,
configurable: true,
writable: true,
value: new Map()
});
Object.defineProperty(this, "_keyedValidators", {
enumerable: true,
configurable: true,
writable: true,
value: new Map()
});
Object.defineProperty(this, "_reactiveFormFieldMap", {
enumerable: true,
configurable: true,
writable: true,
value: shallowReactive(new Map())
});
Object.defineProperty(this, "tryGetSimpleValidators", {
enumerable: true,
configurable: true,
writable: true,
value: tryGet(this._simpleValidators)
});
Object.defineProperty(this, "trySetKeyedValidators", {
enumerable: true,
configurable: true,
writable: true,
value: trySet(this._keyedValidators)
});
Object.defineProperty(this, "tryGetKeyedValidators", {
enumerable: true,
configurable: true,
writable: true,
value: tryGet(this._keyedValidators)
});
Object.defineProperty(this, "submitCount", {
enumerable: true,
configurable: true,
writable: true,
value: 0
});
Object.defineProperty(this, "submitting", {
enumerable: true,
configurable: true,
writable: true,
value: ref(false)
});
Object.defineProperty(this, "errors", {
enumerable: true,
configurable: true,
writable: true,
value: computed(() => {
const errors = [];
for (const formField of this._reactiveFormFieldMap.values()) {
errors.push(...formField.errors.value);
}
return errors;
})
});
}
registerField(uid, name, modelValue, rules) {
const formField = new FormField(name, modelValue, rules);
const simple = {
formField,
keys: [],
vs: [],
rollbacks: []
registerField(uid, name, modelValue, validationBehavior, rules) {
const formField = new FormField(name, modelValue, validationBehavior, rules);
const simpleValidators = {
validators: [],
meta: {
formField,
keys: [],
rollbacks: []
}
};
rules.forEach((rule, ruleNumber) => {
const validate = Form.validateFactory(formField, rule, ruleNumber);
const validator = Form._validatorFactory(formField, ruleNumber);
if (isSimpleRule(rule)) {
if (typeof validate !== 'undefined') {
simple.vs.push(validate);
}
simpleValidators.validators.push(validator);
}
else {
const { key } = rule;
const keyed = {
formField,
v: validate
const keyedValidator = {
validator,
meta: {
formField
}
};
const rollback = () => {
this.tryGetKeyedSet({
success: keyedSet => {
keyedSet.delete(keyed);
if (keyedSet.size === 0) {
this.keyedSetMap.delete(key);
this.tryGetKeyedValidators({
success: keyedValidators => {
keyedValidators.delete(keyedValidator);
if (keyedValidators.size === 0) {
this._keyedValidators.delete(key);
}

@@ -460,25 +719,35 @@ }

};
simple.keys.push(key);
simple.rollbacks.push(rollback);
this.trySetKeyedSet({
failure: keyedSet => keyedSet.add(keyed)
})(key, new Set([keyed]));
simpleValidators.meta.keys.push(key);
simpleValidators.meta.rollbacks.push(rollback);
this.trySetKeyedValidators({
failure: keyedValidators => keyedValidators.add(keyedValidator)
})(key, new Set([keyedValidator]));
}
});
this.simpleMap.set(uid, simple);
this.reactiveFormFieldMap.set(uid, formField);
this.formFields.value.add(formField);
this._simpleValidators.set(uid, simpleValidators);
this._reactiveFormFieldMap.set(uid, formField);
return formField;
}
validate(uid) {
const simple = this.simpleMap.get(uid);
if (simple && simple.formField.touched) {
return Promise.allSettled([
...simple.vs.map(v => v([simple.formField.modelValue])),
...this.getPromisesForKeys(simple.keys)
]);
validate(uid, force = false) {
const simpleValidators = this._simpleValidators.get(uid);
if (simpleValidators) {
const { validators, meta } = simpleValidators;
const shouldValidate = meta.formField.shouldValidate(this);
if (force) {
return Promise.allSettled([
...validators.map(v => v([meta.formField.modelValue])),
...this._getValidationResultsForKeys(meta.keys)
]);
}
if (shouldValidate) {
return Promise.allSettled([
...validators.map(v => v([meta.formField.modelValue])),
...this._getValidationResultsForKeys(meta.keys)
]);
}
}
}
async validateAll(names) {
const settledResults = await Promise.allSettled(this.getPromisesForNames(names));
this.submitCount++;
const settledResults = await Promise.allSettled(this._getValidationResultsForNames(names));
for (const result of settledResults) {

@@ -491,29 +760,24 @@ if (result.status === 'rejected') {

onDelete(uid) {
this.tryGetSimple({
success: ({ rollbacks, formField }) => {
this.formFields.value.delete(formField);
rollbacks.forEach(r => r());
this.tryGetSimpleValidators({
success: ({ meta }) => {
meta.rollbacks.forEach(r => r());
}
})(uid);
this.simpleMap.delete(uid);
this.reactiveFormFieldMap.delete(uid);
this._simpleValidators.delete(uid);
this._reactiveFormFieldMap.delete(uid);
}
resetFields(toDefaultValues = true) {
for (const { formField } of this.simpleMap.values()) {
formField.reset(toDefaultValues);
resetFields(toDefaultValues) {
for (const { meta } of this._simpleValidators.values()) {
meta.formField.reset(toDefaultValues);
}
}
getPromisesForKeys(keys) {
const promises = [];
_getValidationResultsForKeys(keys) {
const validationResults = [];
for (const key of keys) {
this.tryGetKeyedSet({
success: keyedSet => {
if (this.isEveryFormFieldTouchedWith(key)) {
const values = [...keyedSet.values()];
const modelValues = values.map(({ formField }) => formField.modelValue);
const vs = values
.map(({ v }) => v)
.filter(isDefined)
.map(v => v(modelValues));
promises.push(...vs);
this.tryGetKeyedValidators({
success: keyedValidators => {
if (this._shouldEveryFieldValidate(keyedValidators)) {
const values = [...keyedValidators.values()];
const modelValues = values.map(({ meta }) => meta.formField.modelValue);
validationResults.push(...values.map(({ validator }) => validator(modelValues)));
}

@@ -523,82 +787,40 @@ }

}
return promises;
return validationResults;
}
getPromisesForNames(names) {
const promises = [];
if (typeof names === 'undefined') {
for (const { formField, vs } of this.simpleMap.values()) {
formField.touched = true;
promises.push(...vs.map(v => v([formField.modelValue])));
_getValidationResultsForNames(names) {
const validationResults = [];
if (names === undefined) {
for (const { validators, meta } of this._simpleValidators.values()) {
meta.formField.touched = true;
validationResults.push(...validators.map(v => v([meta.formField.modelValue])));
}
promises.push(...this.getPromisesForKeys(this.keyedSetMap.keys()));
validationResults.push(...this._getValidationResultsForKeys(this._keyedValidators.keys()));
}
else if (names.length > 0) {
const nameSet = new Set(names);
for (const { formField, keys, vs } of this.simpleMap.values()) {
if (nameSet.has(formField.name)) {
formField.touched = true;
promises.push(...vs.map(v => v([formField.modelValue])));
promises.push(...this.getPromisesForKeys(keys));
const uniqueNames = new Set(names);
for (const { validators, meta } of this._simpleValidators.values()) {
meta.formField.touched = true;
if (uniqueNames.has(meta.formField.name)) {
validationResults.push(...validators.map(v => v([meta.formField.modelValue])));
validationResults.push(...this._getValidationResultsForKeys(meta.keys));
}
}
}
return promises;
return validationResults;
}
isEveryFormFieldTouchedWith(key) {
let everyFormFieldIsTouched = true;
this.tryGetKeyedSet({
success: keyedSet => {
for (const { formField } of keyedSet) {
if (!formField.touched) {
everyFormFieldIsTouched = false;
break;
}
}
_shouldEveryFieldValidate(keyedValidators) {
let shouldEveryFieldValidate = true;
const mostRestrictiveValidationBehavior = getMostRestrictiveValidationBehavior(this, keyedValidators);
for (const { meta } of keyedValidators) {
if (!meta.formField.shouldValidate(this, mostRestrictiveValidationBehavior)) {
shouldEveryFieldValidate = false;
}
})(key);
return everyFormFieldIsTouched;
}
return shouldEveryFieldValidate;
}
static validateFactory(formField, rule, ruleNumber) {
const buffer = new LinkedList();
const setError = (formField, ruleNumber, error) => {
if (typeof error === 'string' && formField.touched) {
formField.setError(ruleNumber, error);
throw error;
}
else {
formField.setError(ruleNumber, null);
}
static _validatorFactory(formField, ruleNumber) {
const curriedValidator = (formField, ruleNumber) => modelValues => {
return formField.validate(ruleNumber, modelValues);
};
const validator = (formField, rule, ruleNumber) => async (modelValues) => {
let error;
const ruleResult = rule(...modelValues.map(unref));
if (typeof (ruleResult === null || ruleResult === void 0 ? void 0 : ruleResult.then) === 'function') {
formField.rulesValidating.value++;
const node = buffer.addLast(false);
if (node.prev) {
node.prev.value = true;
}
try {
error = await ruleResult;
}
catch (err) {
error = err;
}
buffer.remove(node);
formField.rulesValidating.value--;
if (!node.value) {
setError(formField, ruleNumber, error);
}
}
else {
error = ruleResult;
setError(formField, ruleNumber, error);
}
};
if (isSimpleRule(rule)) {
return validator(formField, rule, ruleNumber);
}
else if (rule.rule) {
return validator(formField, rule.rule, ruleNumber);
}
return curriedValidator(formField, ruleNumber);
}

@@ -609,3 +831,3 @@ }

*
* @param formData The structure of your Form Data.
* @param formData - The structure of your Form Data.
* @description

@@ -617,3 +839,3 @@ * Vue composition function for Form Validation.

* For better type inference, consider defining the structure
* of your `formData` upfront and pass it as the generic parameter `T`:
* of your `formData` upfront and pass it as the generic parameter `FormData`:
* ```

@@ -636,3 +858,2 @@ * type FormData = {

form: transformedFormData,
formFields: form.formFields,
submitting: form.submitting,

@@ -655,21 +876,22 @@ errors: form.errors,

}
if (formData) {
if (formData === undefined) {
form.resetFields(true);
}
else {
form.resetFields(false);
resetFields(formData, transformedFormData);
}
else {
form.resetFields();
}
},
add(path$1, value) {
const lastKey = path$1[path$1.length - 1];
if (typeof lastKey !== 'undefined') {
if (lastKey !== undefined) {
const box = { [lastKey]: value };
transformFormData(form, box);
const x = path(path$1, transformedFormData);
if (Array.isArray(x)) {
x.push(box[lastKey]);
const transformedField = box[lastKey];
const valueAtPath = path(path$1, transformedFormData);
if (Array.isArray(valueAtPath)) {
valueAtPath.push(transformedField);
}
else {
set(transformedFormData, path$1, box[lastKey]);
set(transformedFormData, path$1, transformedField);
}

@@ -680,15 +902,17 @@ }

const lastKey = path$1.pop();
if (typeof lastKey !== 'undefined' && path$1.length === 0) {
cleanupForm(form, transformedFormData[lastKey]);
delete transformedFormData[lastKey];
}
else if (typeof lastKey !== 'undefined') {
const value = path(path$1, transformedFormData);
if (Array.isArray(value)) {
const deleted = value.splice(+lastKey, 1);
cleanupForm(form, deleted);
if (lastKey !== undefined) {
if (path$1.length === 0) {
cleanupForm(form, transformedFormData[lastKey]);
delete transformedFormData[lastKey];
}
else {
cleanupForm(form, value[lastKey]);
delete value[lastKey];
const valueAtPath = path(path$1, transformedFormData);
if (Array.isArray(valueAtPath)) {
const deletedFormData = valueAtPath.splice(+lastKey, 1);
cleanupForm(form, deletedFormData);
}
else {
cleanupForm(form, valueAtPath[lastKey]);
delete valueAtPath[lastKey];
}
}

@@ -695,0 +919,0 @@ }

{
"name": "vue3-form-validation",
"version": "4.1.1",
"version": "5.0.0-beta.1",
"description": "Vue composition function for Form Validation",

@@ -5,0 +5,0 @@ "author": {

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc