@vuehookform/core 0.3.3 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,21 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  let vue = require("vue");
3
+ var pathCache = /* @__PURE__ */ new Map();
4
+ var PATH_CACHE_MAX_SIZE = 256;
5
+ function getPathSegments(path) {
6
+ let segments = pathCache.get(path);
7
+ if (segments) return segments;
8
+ segments = path.split(".");
9
+ if (pathCache.size >= PATH_CACHE_MAX_SIZE) {
10
+ const firstKey = pathCache.keys().next().value;
11
+ if (firstKey !== void 0) pathCache.delete(firstKey);
12
+ }
13
+ pathCache.set(path, segments);
14
+ return segments;
15
+ }
3
16
  function get(obj, path) {
4
17
  if (!path || obj === null || obj === void 0) return obj;
5
- const keys = path.split(".");
18
+ const keys = getPathSegments(path);
6
19
  let result = obj;
7
20
  for (const key of keys) {
8
21
  if (result === null || result === void 0) return;
@@ -12,7 +25,7 @@ function get(obj, path) {
12
25
  }
13
26
  function set(obj, path, value) {
14
27
  if (!path) return;
15
- const keys = path.split(".");
28
+ const keys = getPathSegments(path).slice();
16
29
  const UNSAFE_KEYS = [
17
30
  "__proto__",
18
31
  "constructor",
@@ -37,7 +50,7 @@ function set(obj, path, value) {
37
50
  }
38
51
  function unset(obj, path) {
39
52
  if (!path) return;
40
- const keys = path.split(".");
53
+ const keys = getPathSegments(path).slice();
41
54
  const lastKey = keys.pop();
42
55
  let current = obj;
43
56
  for (const key of keys) {
@@ -53,6 +66,15 @@ function generateId() {
53
66
  const random = Math.random().toString(36).substring(2, 11);
54
67
  return `field_${Date.now()}_${idCounter++}_${random}`;
55
68
  }
69
+ function deepClone(obj) {
70
+ if (obj === null || obj === void 0) return obj;
71
+ if (typeof obj !== "object") return obj;
72
+ if (obj instanceof Date) return new Date(obj.getTime());
73
+ if (Array.isArray(obj)) return obj.map((item) => deepClone(item));
74
+ const cloned = {};
75
+ for (const key in obj) if (Object.prototype.hasOwnProperty.call(obj, key)) cloned[key] = deepClone(obj[key]);
76
+ return cloned;
77
+ }
56
78
  var proc = globalThis.process;
57
79
  const __DEV__ = proc?.env?.NODE_ENV !== "production";
58
80
  var warnedMessages = /* @__PURE__ */ new Set();
@@ -82,7 +104,7 @@ function traverseSchemaPath(schema, path) {
82
104
  const segment = segments[i];
83
105
  if (!segment) continue;
84
106
  currentSchema = unwrapSchema(currentSchema);
85
- if (isZodObject(currentSchema)) {
107
+ if (isZodObject$1(currentSchema)) {
86
108
  const shape = currentSchema.shape;
87
109
  if (segment in shape) {
88
110
  const nextSchema = shape[segment];
@@ -97,7 +119,7 @@ function traverseSchemaPath(schema, path) {
97
119
  segmentIndex: i
98
120
  };
99
121
  }
100
- if (isZodArray(currentSchema) && /^\d+$/.test(segment)) {
122
+ if (isZodArray$1(currentSchema) && /^\d+$/.test(segment)) {
101
123
  currentSchema = currentSchema.element;
102
124
  continue;
103
125
  }
@@ -127,7 +149,7 @@ function isArrayFieldInSchema(schema, path) {
127
149
  try {
128
150
  const result = traverseSchemaPath(schema, path);
129
151
  if ("error" in result) return null;
130
- return isZodArray(unwrapSchema(result.schema));
152
+ return isZodArray$1(unwrapSchema(result.schema));
131
153
  } catch {
132
154
  return null;
133
155
  }
@@ -180,22 +202,22 @@ function warnArrayIndexOutOfBounds(operation, path, index, length) {
180
202
  if (!__DEV__) return;
181
203
  warn(`${operation}() on "${path}": Index ${index} is out of bounds (array length: ${length}). Operation was silently ignored.`);
182
204
  }
183
- function getDefProp(schema, prop) {
205
+ function getDefProp$1(schema, prop) {
184
206
  return schema.def[prop];
185
207
  }
186
208
  function getTypeName(schema) {
187
- return getDefProp(schema, "typeName");
209
+ return getDefProp$1(schema, "typeName");
188
210
  }
189
- function isZodObject(schema) {
211
+ function isZodObject$1(schema) {
190
212
  return getTypeName(schema) === "ZodObject";
191
213
  }
192
- function isZodArray(schema) {
214
+ function isZodArray$1(schema) {
193
215
  return getTypeName(schema) === "ZodArray";
194
216
  }
195
217
  function unwrapSchema(schema) {
196
218
  const typeName = getTypeName(schema);
197
- const innerType = getDefProp(schema, "innerType");
198
- const schemaType = getDefProp(schema, "schema");
219
+ const innerType = getDefProp$1(schema, "innerType");
220
+ const schemaType = getDefProp$1(schema, "schema");
199
221
  if ((typeName === "ZodOptional" || typeName === "ZodNullable" || typeName === "ZodDefault") && innerType) return unwrapSchema(innerType);
200
222
  if (typeName === "ZodEffects" && schemaType) return unwrapSchema(schemaType);
201
223
  return schema;
@@ -228,7 +250,7 @@ function createFormContext(options) {
228
250
  const submitCount = (0, vue.ref)(0);
229
251
  const defaultValuesError = (0, vue.ref)(null);
230
252
  const isSubmitSuccessful = (0, vue.ref)(false);
231
- const validatingFields = (0, vue.shallowRef)({});
253
+ const validatingFields = (0, vue.shallowRef)(/* @__PURE__ */ new Set());
232
254
  const externalErrors = (0, vue.shallowRef)({});
233
255
  const errorDelayTimers = /* @__PURE__ */ new Map();
234
256
  const pendingErrors = /* @__PURE__ */ new Map();
@@ -239,6 +261,10 @@ function createFormContext(options) {
239
261
  const debounceTimers = /* @__PURE__ */ new Map();
240
262
  const validationRequestIds = /* @__PURE__ */ new Map();
241
263
  const resetGeneration = (0, vue.ref)(0);
264
+ const dirtyFieldCount = (0, vue.ref)(0);
265
+ const touchedFieldCount = (0, vue.ref)(0);
266
+ const validationCache = /* @__PURE__ */ new Map();
267
+ const schemaValidationTimers = /* @__PURE__ */ new Map();
242
268
  const isDisabled = (0, vue.ref)(false);
243
269
  if (options.disabled !== void 0) {
244
270
  isDisabled.value = (0, vue.toValue)(options.disabled) ?? false;
@@ -296,24 +322,136 @@ function createFormContext(options) {
296
322
  validationRequestIds,
297
323
  resetGeneration,
298
324
  isDisabled,
325
+ dirtyFieldCount,
326
+ touchedFieldCount,
327
+ validationCache,
328
+ schemaValidationTimers,
299
329
  options
300
330
  };
301
331
  }
332
+ var uniqueIdCounter = 0;
333
+ function hashValue(value) {
334
+ if (value === null) return "null";
335
+ if (value === void 0) return "undefined";
336
+ const type = typeof value;
337
+ if (type === "string") return `s:${value}`;
338
+ if (type === "number") return `n:${value}`;
339
+ if (type === "boolean") return `b:${value}`;
340
+ if (type === "object") try {
341
+ return `o:${JSON.stringify(value)}`;
342
+ } catch {
343
+ return `o:_${++uniqueIdCounter}`;
344
+ }
345
+ return `x:_${++uniqueIdCounter}`;
346
+ }
347
+ function getDefType(schema) {
348
+ return schema._def?.type;
349
+ }
350
+ function getDefProp(schema, prop) {
351
+ return schema._def?.[prop];
352
+ }
353
+ function isZodObject(schema) {
354
+ return getDefType(schema) === "object";
355
+ }
356
+ function isZodArray(schema) {
357
+ return getDefType(schema) === "array";
358
+ }
359
+ function hasChecks(schema) {
360
+ const checks = getDefProp(schema, "checks");
361
+ return Array.isArray(checks) && checks.length > 0;
362
+ }
363
+ function unwrapNonEffects(schema) {
364
+ const type = getDefType(schema);
365
+ const innerType = getDefProp(schema, "innerType");
366
+ if ((type === "optional" || type === "nullable" || type === "default") && innerType) return unwrapNonEffects(innerType);
367
+ return schema;
368
+ }
369
+ var analysisCache = /* @__PURE__ */ new WeakMap();
370
+ function hasRootEffects(schema) {
371
+ return hasChecks(schema);
372
+ }
373
+ function extractSubSchema(schema, path) {
374
+ const segments = path.split(".");
375
+ let currentSchema = schema;
376
+ let hasEffects = false;
377
+ for (const segment of segments) {
378
+ if (!segment) continue;
379
+ if (hasChecks(currentSchema)) hasEffects = true;
380
+ const unwrapped = unwrapNonEffects(currentSchema);
381
+ if (hasChecks(unwrapped)) hasEffects = true;
382
+ if (isZodObject(unwrapped)) {
383
+ const shape = getDefProp(unwrapped, "shape");
384
+ if (!shape || !(segment in shape)) return null;
385
+ currentSchema = shape[segment];
386
+ } else if (isZodArray(unwrapped) && /^\d+$/.test(segment)) {
387
+ const element = getDefProp(unwrapped, "element");
388
+ if (!element) return null;
389
+ currentSchema = element;
390
+ } else return null;
391
+ }
392
+ const finalUnwrapped = unwrapNonEffects(currentSchema);
393
+ const finalChecks = getDefProp(finalUnwrapped, "checks");
394
+ if (finalChecks) {
395
+ for (const check of finalChecks) if (check && typeof check === "object" && "type" in check && check.type === "custom") {
396
+ hasEffects = true;
397
+ break;
398
+ }
399
+ }
400
+ return {
401
+ schema: finalUnwrapped,
402
+ hasEffects
403
+ };
404
+ }
405
+ function analyzeSchemaPath(schema, path) {
406
+ let cache = analysisCache.get(schema);
407
+ if (!cache) {
408
+ cache = /* @__PURE__ */ new Map();
409
+ analysisCache.set(schema, cache);
410
+ }
411
+ const cached = cache.get(path);
412
+ if (cached) return cached;
413
+ if (hasRootEffects(schema)) {
414
+ const result$1 = {
415
+ canPartialValidate: false,
416
+ reason: "root-checks"
417
+ };
418
+ cache.set(path, result$1);
419
+ return result$1;
420
+ }
421
+ const extracted = extractSubSchema(schema, path);
422
+ if (!extracted) {
423
+ const result$1 = {
424
+ canPartialValidate: false,
425
+ reason: "invalid-path"
426
+ };
427
+ cache.set(path, result$1);
428
+ return result$1;
429
+ }
430
+ if (extracted.hasEffects) {
431
+ const result$1 = {
432
+ canPartialValidate: false,
433
+ reason: "path-checks"
434
+ };
435
+ cache.set(path, result$1);
436
+ return result$1;
437
+ }
438
+ const result = {
439
+ canPartialValidate: true,
440
+ subSchema: extracted.schema
441
+ };
442
+ cache.set(path, result);
443
+ return result;
444
+ }
302
445
  function clearFieldErrors$1(errors, fieldPath) {
303
446
  const newErrors = { ...errors };
304
447
  for (const key of Object.keys(newErrors)) if (key === fieldPath || key.startsWith(`${fieldPath}.`)) delete newErrors[key];
305
448
  return newErrors;
306
449
  }
307
450
  function setValidating(ctx, fieldPath, isValidating) {
308
- if (isValidating) ctx.validatingFields.value = {
309
- ...ctx.validatingFields.value,
310
- [fieldPath]: true
311
- };
312
- else {
313
- const newValidating = { ...ctx.validatingFields.value };
314
- delete newValidating[fieldPath];
315
- ctx.validatingFields.value = newValidating;
316
- }
451
+ const newSet = new Set(ctx.validatingFields.value);
452
+ if (isValidating) newSet.add(fieldPath);
453
+ else newSet.delete(fieldPath);
454
+ ctx.validatingFields.value = newSet;
317
455
  }
318
456
  function groupErrorsByPath(issues) {
319
457
  const grouped = /* @__PURE__ */ new Map();
@@ -355,6 +493,18 @@ function createValidation(ctx) {
355
493
  if (!ctx.options.shouldUseNativeValidation) return;
356
494
  for (const [path] of ctx.fieldRefs) applyNativeValidation(path, null);
357
495
  }
496
+ function scheduleErrorsBatch(errors) {
497
+ if ((ctx.options.delayError || 0) <= 0) {
498
+ const newErrors = { ...ctx.errors.value };
499
+ for (const [fieldPath, error] of errors) {
500
+ set(newErrors, fieldPath, error);
501
+ applyNativeValidation(fieldPath, typeof error === "string" ? error : error.message);
502
+ }
503
+ ctx.errors.value = newErrors;
504
+ return;
505
+ }
506
+ for (const [fieldPath, error] of errors) scheduleError(fieldPath, error);
507
+ }
358
508
  function scheduleError(fieldPath, error) {
359
509
  const delayMs = ctx.options.delayError || 0;
360
510
  const errorMessage = typeof error === "string" ? error : error.message;
@@ -396,42 +546,111 @@ function createValidation(ctx) {
396
546
  ctx.errorDelayTimers.clear();
397
547
  ctx.pendingErrors.clear();
398
548
  }
549
+ async function validateFieldPartial(fieldPath, subSchema, valueHash, criteriaMode, generationAtStart) {
550
+ const fieldValue = get(ctx.formData, fieldPath);
551
+ setValidating(ctx, fieldPath, true);
552
+ try {
553
+ const result = await subSchema.safeParseAsync(fieldValue);
554
+ if (ctx.resetGeneration.value !== generationAtStart) return true;
555
+ if (result.success) {
556
+ ctx.errors.value = cancelError(fieldPath);
557
+ if (valueHash) ctx.validationCache.set(fieldPath, {
558
+ hash: valueHash,
559
+ isValid: true
560
+ });
561
+ return true;
562
+ }
563
+ const fieldErrors = result.error.issues.map((issue) => ({
564
+ ...issue,
565
+ path: fieldPath.split(".").concat(issue.path.map(String))
566
+ }));
567
+ ctx.errors.value = cancelError(fieldPath);
568
+ const grouped = groupErrorsByPath(fieldErrors);
569
+ const errorBatch = [];
570
+ for (const [path, errors] of grouped) errorBatch.push([path, createFieldError(errors, criteriaMode)]);
571
+ scheduleErrorsBatch(errorBatch);
572
+ if (valueHash) ctx.validationCache.set(fieldPath, {
573
+ hash: valueHash,
574
+ isValid: false
575
+ });
576
+ return false;
577
+ } finally {
578
+ setValidating(ctx, fieldPath, false);
579
+ }
580
+ }
581
+ async function validateFieldFull(fieldPath, valueHash, criteriaMode, generationAtStart) {
582
+ setValidating(ctx, fieldPath, true);
583
+ try {
584
+ const result = await ctx.options.schema.safeParseAsync(ctx.formData);
585
+ if (ctx.resetGeneration.value !== generationAtStart) return true;
586
+ if (result.success) {
587
+ ctx.errors.value = cancelError(fieldPath);
588
+ if (valueHash) ctx.validationCache.set(fieldPath, {
589
+ hash: valueHash,
590
+ isValid: true
591
+ });
592
+ return true;
593
+ }
594
+ const fieldErrors = result.error.issues.filter((issue) => {
595
+ const path = issue.path.join(".");
596
+ return path === fieldPath || path.startsWith(`${fieldPath}.`);
597
+ });
598
+ if (fieldErrors.length === 0) {
599
+ ctx.errors.value = cancelError(fieldPath);
600
+ if (valueHash) ctx.validationCache.set(fieldPath, {
601
+ hash: valueHash,
602
+ isValid: true
603
+ });
604
+ return true;
605
+ }
606
+ ctx.errors.value = cancelError(fieldPath);
607
+ const grouped = groupErrorsByPath(fieldErrors);
608
+ const errorBatch = [];
609
+ for (const [path, errors] of grouped) errorBatch.push([path, createFieldError(errors, criteriaMode)]);
610
+ scheduleErrorsBatch(errorBatch);
611
+ if (valueHash) ctx.validationCache.set(fieldPath, {
612
+ hash: valueHash,
613
+ isValid: false
614
+ });
615
+ return false;
616
+ } finally {
617
+ setValidating(ctx, fieldPath, false);
618
+ }
619
+ }
399
620
  async function validate(fieldPath) {
400
621
  const generationAtStart = ctx.resetGeneration.value;
401
622
  const criteriaMode = ctx.options.criteriaMode || "firstError";
402
- const validatingKey = fieldPath || "_form";
623
+ let valueHash;
624
+ if (fieldPath) {
625
+ valueHash = hashValue(get(ctx.formData, fieldPath));
626
+ const cached = ctx.validationCache.get(fieldPath);
627
+ if (cached && cached.hash === valueHash) {
628
+ if (cached.isValid) ctx.errors.value = cancelError(fieldPath);
629
+ return cached.isValid;
630
+ }
631
+ const analysis = analyzeSchemaPath(ctx.options.schema, fieldPath);
632
+ if (analysis.canPartialValidate && analysis.subSchema) return validateFieldPartial(fieldPath, analysis.subSchema, valueHash, criteriaMode, generationAtStart);
633
+ return validateFieldFull(fieldPath, valueHash, criteriaMode, generationAtStart);
634
+ }
635
+ const validatingKey = "_form";
403
636
  setValidating(ctx, validatingKey, true);
404
637
  try {
405
638
  const result = await ctx.options.schema.safeParseAsync(ctx.formData);
406
639
  if (ctx.resetGeneration.value !== generationAtStart) return true;
407
640
  if (result.success) {
408
- if (fieldPath) ctx.errors.value = cancelError(fieldPath);
409
- else {
410
- clearAllPendingErrors();
411
- ctx.errors.value = {};
412
- clearAllNativeValidation();
413
- }
641
+ clearAllPendingErrors();
642
+ ctx.errors.value = {};
643
+ clearAllNativeValidation();
644
+ ctx.validationCache.clear();
414
645
  return true;
415
646
  }
416
- const zodErrors = result.error.issues;
417
- if (fieldPath) {
418
- const fieldErrors = zodErrors.filter((issue) => {
419
- const path = issue.path.join(".");
420
- return path === fieldPath || path.startsWith(`${fieldPath}.`);
421
- });
422
- if (fieldErrors.length === 0) {
423
- ctx.errors.value = cancelError(fieldPath);
424
- return true;
425
- }
426
- ctx.errors.value = cancelError(fieldPath);
427
- const grouped$1 = groupErrorsByPath(fieldErrors);
428
- for (const [path, errors] of grouped$1) scheduleError(path, createFieldError(errors, criteriaMode));
429
- return false;
430
- }
431
647
  clearAllPendingErrors();
432
648
  ctx.errors.value = {};
433
- const grouped = groupErrorsByPath(zodErrors);
434
- for (const [path, errors] of grouped) scheduleError(path, createFieldError(errors, criteriaMode));
649
+ const grouped = groupErrorsByPath(result.error.issues);
650
+ const errorBatch = [];
651
+ for (const [path, errors] of grouped) errorBatch.push([path, createFieldError(errors, criteriaMode)]);
652
+ scheduleErrorsBatch(errorBatch);
653
+ ctx.validationCache.clear();
435
654
  return false;
436
655
  } finally {
437
656
  setValidating(ctx, validatingKey, false);
@@ -442,6 +661,48 @@ function createValidation(ctx) {
442
661
  clearAllPendingErrors
443
662
  };
444
663
  }
664
+ function markFieldDirty(dirtyFields, dirtyFieldCount, fieldName) {
665
+ if (dirtyFields.value[fieldName]) return;
666
+ dirtyFields.value = {
667
+ ...dirtyFields.value,
668
+ [fieldName]: true
669
+ };
670
+ dirtyFieldCount.value++;
671
+ }
672
+ function markFieldTouched(touchedFields, touchedFieldCount, fieldName) {
673
+ if (touchedFields.value[fieldName]) return;
674
+ touchedFields.value = {
675
+ ...touchedFields.value,
676
+ [fieldName]: true
677
+ };
678
+ touchedFieldCount.value++;
679
+ }
680
+ function clearFieldDirty(dirtyFields, dirtyFieldCount, fieldName) {
681
+ if (!(fieldName in dirtyFields.value)) return;
682
+ const newDirty = { ...dirtyFields.value };
683
+ delete newDirty[fieldName];
684
+ dirtyFields.value = newDirty;
685
+ dirtyFieldCount.value--;
686
+ }
687
+ function clearFieldTouched(touchedFields, touchedFieldCount, fieldName) {
688
+ if (!(fieldName in touchedFields.value)) return;
689
+ const newTouched = { ...touchedFields.value };
690
+ delete newTouched[fieldName];
691
+ touchedFields.value = newTouched;
692
+ touchedFieldCount.value--;
693
+ }
694
+ function clearFieldErrors(errors, fieldName) {
695
+ const currentErrors = errors.value;
696
+ const keys = Object.keys(currentErrors);
697
+ if (keys.length === 0) return;
698
+ const prefix = `${fieldName}.`;
699
+ const keysToDelete = [];
700
+ for (const key of keys) if (key === fieldName || key.startsWith(prefix)) keysToDelete.push(key);
701
+ if (keysToDelete.length === 0) return;
702
+ const newErrors = { ...currentErrors };
703
+ for (const key of keysToDelete) delete newErrors[key];
704
+ errors.value = newErrors;
705
+ }
445
706
  var validationRequestCounter = 0;
446
707
  function createFieldRegistration(ctx, validate) {
447
708
  function register(name, registerOptions) {
@@ -483,16 +744,30 @@ function createFieldRegistration(ctx, validate) {
483
744
  const target = e.target;
484
745
  const value = target.type === "checkbox" ? target.checked : target.value;
485
746
  set(ctx.formData, name, value);
486
- ctx.dirtyFields.value = {
487
- ...ctx.dirtyFields.value,
488
- [name]: true
489
- };
747
+ markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
748
+ const fieldOpts = ctx.fieldOptions.get(name);
490
749
  if (ctx.options.mode === "onChange" || ctx.options.mode === "onTouched" && ctx.touchedFields.value[name] || ctx.touchedFields.value[name] && ctx.options.reValidateMode === "onChange") {
491
- await validate(name);
492
- const fieldOpts$1 = ctx.fieldOptions.get(name);
493
- if (fieldOpts$1?.deps && fieldOpts$1.deps.length > 0) for (const depField of fieldOpts$1.deps) validate(depField);
750
+ const validationDebounceMs = ctx.options.validationDebounce || 0;
751
+ if (validationDebounceMs > 0) {
752
+ const existingTimer = ctx.schemaValidationTimers.get(name);
753
+ if (existingTimer) clearTimeout(existingTimer);
754
+ const timer = setTimeout(async () => {
755
+ ctx.schemaValidationTimers.delete(name);
756
+ await validate(name);
757
+ if (fieldOpts?.deps && fieldOpts.deps.length > 0) {
758
+ const uniqueDeps = [...new Set(fieldOpts.deps)];
759
+ await Promise.all(uniqueDeps.map((depField) => validate(depField)));
760
+ }
761
+ }, validationDebounceMs);
762
+ ctx.schemaValidationTimers.set(name, timer);
763
+ } else {
764
+ await validate(name);
765
+ if (fieldOpts?.deps && fieldOpts.deps.length > 0) {
766
+ const uniqueDeps = [...new Set(fieldOpts.deps)];
767
+ await Promise.all(uniqueDeps.map((depField) => validate(depField)));
768
+ }
769
+ }
494
770
  }
495
- const fieldOpts = ctx.fieldOptions.get(name);
496
771
  if (fieldOpts?.validate && !fieldOpts.disabled) {
497
772
  const requestId = ++validationRequestCounter;
498
773
  ctx.validationRequestIds.set(name, requestId);
@@ -501,23 +776,24 @@ function createFieldRegistration(ctx, validate) {
501
776
  if (debounceMs > 0) {
502
777
  const existingTimer = ctx.debounceTimers.get(name);
503
778
  if (existingTimer) clearTimeout(existingTimer);
504
- const timer = setTimeout(() => {
779
+ const timer = setTimeout(async () => {
505
780
  ctx.debounceTimers.delete(name);
506
- runCustomValidation(name, value, requestId, resetGenAtStart);
781
+ await runCustomValidation(name, value, requestId, resetGenAtStart);
782
+ ctx.validationRequestIds.delete(name);
507
783
  }, debounceMs);
508
784
  ctx.debounceTimers.set(name, timer);
509
785
  } else await runCustomValidation(name, value, requestId, resetGenAtStart);
510
786
  }
511
787
  };
512
788
  const onBlur = async (_e) => {
513
- ctx.touchedFields.value = {
514
- ...ctx.touchedFields.value,
515
- [name]: true
516
- };
789
+ markFieldTouched(ctx.touchedFields, ctx.touchedFieldCount, name);
517
790
  if (ctx.options.mode === "onBlur" || ctx.options.mode === "onTouched" || ctx.submitCount.value > 0 && ctx.options.reValidateMode === "onBlur") {
518
791
  await validate(name);
519
792
  const fieldOpts = ctx.fieldOptions.get(name);
520
- if (fieldOpts?.deps && fieldOpts.deps.length > 0) for (const depField of fieldOpts.deps) validate(depField);
793
+ if (fieldOpts?.deps && fieldOpts.deps.length > 0) {
794
+ const uniqueDeps = [...new Set(fieldOpts.deps)];
795
+ await Promise.all(uniqueDeps.map((depField) => validate(depField)));
796
+ }
521
797
  }
522
798
  };
523
799
  const refCallback = (el) => {
@@ -539,18 +815,17 @@ function createFieldRegistration(ctx, validate) {
539
815
  clearTimeout(timer);
540
816
  ctx.debounceTimers.delete(name);
541
817
  }
818
+ const schemaTimer = ctx.schemaValidationTimers.get(name);
819
+ if (schemaTimer) {
820
+ clearTimeout(schemaTimer);
821
+ ctx.schemaValidationTimers.delete(name);
822
+ }
542
823
  ctx.validationRequestIds.delete(name);
543
824
  if (opts?.shouldUnregister ?? ctx.options.shouldUnregister ?? false) {
544
825
  unset(ctx.formData, name);
545
- const newErrors = { ...ctx.errors.value };
546
- delete newErrors[name];
547
- ctx.errors.value = newErrors;
548
- const newTouched = { ...ctx.touchedFields.value };
549
- delete newTouched[name];
550
- ctx.touchedFields.value = newTouched;
551
- const newDirty = { ...ctx.dirtyFields.value };
552
- delete newDirty[name];
553
- ctx.dirtyFields.value = newDirty;
826
+ clearFieldErrors(ctx.errors, name);
827
+ clearFieldTouched(ctx.touchedFields, ctx.touchedFieldCount, name);
828
+ clearFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
554
829
  ctx.fieldRefs.delete(name);
555
830
  ctx.fieldOptions.delete(name);
556
831
  ctx.fieldHandlers.delete(name);
@@ -574,10 +849,7 @@ function createFieldRegistration(ctx, validate) {
574
849
  get: () => get(ctx.formData, name),
575
850
  set: (val) => {
576
851
  set(ctx.formData, name, val);
577
- ctx.dirtyFields.value = {
578
- ...ctx.dirtyFields.value,
579
- [name]: true
580
- };
852
+ markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
581
853
  }
582
854
  }) }
583
855
  };
@@ -585,21 +857,9 @@ function createFieldRegistration(ctx, validate) {
585
857
  function unregister(name, options) {
586
858
  const opts = options || {};
587
859
  if (!opts.keepValue) unset(ctx.formData, name);
588
- if (!opts.keepError) {
589
- const newErrors = { ...ctx.errors.value };
590
- delete newErrors[name];
591
- ctx.errors.value = newErrors;
592
- }
593
- if (!opts.keepTouched) {
594
- const newTouched = { ...ctx.touchedFields.value };
595
- delete newTouched[name];
596
- ctx.touchedFields.value = newTouched;
597
- }
598
- if (!opts.keepDirty) {
599
- const newDirty = { ...ctx.dirtyFields.value };
600
- delete newDirty[name];
601
- ctx.dirtyFields.value = newDirty;
602
- }
860
+ if (!opts.keepError) clearFieldErrors(ctx.errors, name);
861
+ if (!opts.keepTouched) clearFieldTouched(ctx.touchedFields, ctx.touchedFieldCount, name);
862
+ if (!opts.keepDirty) clearFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
603
863
  ctx.fieldRefs.delete(name);
604
864
  ctx.fieldOptions.delete(name);
605
865
  ctx.fieldHandlers.delete(name);
@@ -608,6 +868,11 @@ function createFieldRegistration(ctx, validate) {
608
868
  clearTimeout(timer);
609
869
  ctx.debounceTimers.delete(name);
610
870
  }
871
+ const schemaTimer = ctx.schemaValidationTimers.get(name);
872
+ if (schemaTimer) {
873
+ clearTimeout(schemaTimer);
874
+ ctx.schemaValidationTimers.delete(name);
875
+ }
611
876
  ctx.validationRequestIds.delete(name);
612
877
  }
613
878
  return {
@@ -644,6 +909,35 @@ function createFieldArrayManager(ctx, validate, setFocus) {
644
909
  indexCache.set(item.key, idx);
645
910
  });
646
911
  };
912
+ const appendToCache = (startIndex) => {
913
+ const items = fa.items.value;
914
+ for (let i = startIndex; i < items.length; i++) {
915
+ const item = items[i];
916
+ if (item) indexCache.set(item.key, i);
917
+ }
918
+ };
919
+ const updateCacheAfterInsert = (insertIndex, _insertCount) => {
920
+ const items = fa.items.value;
921
+ for (let i = insertIndex; i < items.length; i++) {
922
+ const item = items[i];
923
+ if (item) indexCache.set(item.key, i);
924
+ }
925
+ };
926
+ const swapInCache = (indexA, indexB) => {
927
+ const items = fa.items.value;
928
+ const itemA = items[indexA];
929
+ const itemB = items[indexB];
930
+ if (itemA) indexCache.set(itemA.key, indexA);
931
+ if (itemB) indexCache.set(itemB.key, indexB);
932
+ };
933
+ const updateCacheAfterRemove = (removedKey, startIndex) => {
934
+ indexCache.delete(removedKey);
935
+ const items = fa.items.value;
936
+ for (let i = startIndex; i < items.length; i++) {
937
+ const item = items[i];
938
+ if (item) indexCache.set(item.key, i);
939
+ }
940
+ };
647
941
  const createItem = (key) => ({
648
942
  key,
649
943
  get index() {
@@ -686,11 +980,8 @@ function createFieldArrayManager(ctx, validate, setFocus) {
686
980
  set(ctx.formData, name, newValues);
687
981
  const newItems = values.map(() => createItem(generateId()));
688
982
  fa.items.value = [...fa.items.value, ...newItems];
689
- rebuildIndexCache();
690
- ctx.dirtyFields.value = {
691
- ...ctx.dirtyFields.value,
692
- [name]: true
693
- };
983
+ appendToCache(insertIndex);
984
+ markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
694
985
  if (ctx.options.mode === "onChange") validate(name);
695
986
  handleFocus(insertIndex, values.length, focusOptions);
696
987
  return true;
@@ -711,11 +1002,8 @@ function createFieldArrayManager(ctx, validate, setFocus) {
711
1002
  set(ctx.formData, name, newValues);
712
1003
  const newItems = values.map(() => createItem(generateId()));
713
1004
  fa.items.value = [...newItems, ...fa.items.value];
714
- rebuildIndexCache();
715
- ctx.dirtyFields.value = {
716
- ...ctx.dirtyFields.value,
717
- [name]: true
718
- };
1005
+ updateCacheAfterInsert(0, values.length);
1006
+ markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
719
1007
  if (ctx.options.mode === "onChange") validate(name);
720
1008
  handleFocus(0, values.length, focusOptions);
721
1009
  return true;
@@ -729,10 +1017,7 @@ function createFieldArrayManager(ctx, validate, setFocus) {
729
1017
  const newValues = [...currentValues];
730
1018
  newValues[index] = value;
731
1019
  set(ctx.formData, name, newValues);
732
- ctx.dirtyFields.value = {
733
- ...ctx.dirtyFields.value,
734
- [name]: true
735
- };
1020
+ markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
736
1021
  if (ctx.options.mode === "onChange") validate(name);
737
1022
  return true;
738
1023
  };
@@ -754,11 +1039,8 @@ function createFieldArrayManager(ctx, validate, setFocus) {
754
1039
  set(ctx.formData, name, newValues);
755
1040
  const keyToRemove = fa.items.value[index]?.key;
756
1041
  fa.items.value = fa.items.value.filter((item) => item.key !== keyToRemove);
757
- rebuildIndexCache();
758
- ctx.dirtyFields.value = {
759
- ...ctx.dirtyFields.value,
760
- [name]: true
761
- };
1042
+ if (keyToRemove) updateCacheAfterRemove(keyToRemove, index);
1043
+ markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
762
1044
  if (ctx.options.mode === "onChange") validate(name);
763
1045
  return true;
764
1046
  };
@@ -787,11 +1069,8 @@ function createFieldArrayManager(ctx, validate, setFocus) {
787
1069
  ...newItems,
788
1070
  ...fa.items.value.slice(clampedIndex)
789
1071
  ];
790
- rebuildIndexCache();
791
- ctx.dirtyFields.value = {
792
- ...ctx.dirtyFields.value,
793
- [name]: true
794
- };
1072
+ updateCacheAfterInsert(clampedIndex, values.length);
1073
+ markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
795
1074
  if (ctx.options.mode === "onChange") validate(name);
796
1075
  handleFocus(clampedIndex, values.length, focusOptions);
797
1076
  return true;
@@ -812,12 +1091,9 @@ function createFieldArrayManager(ctx, validate, setFocus) {
812
1091
  newItems[indexA] = itemB;
813
1092
  newItems[indexB] = itemA;
814
1093
  fa.items.value = newItems;
815
- rebuildIndexCache();
1094
+ swapInCache(indexA, indexB);
816
1095
  }
817
- ctx.dirtyFields.value = {
818
- ...ctx.dirtyFields.value,
819
- [name]: true
820
- };
1096
+ markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
821
1097
  if (ctx.options.mode === "onChange") validate(name);
822
1098
  return true;
823
1099
  };
@@ -840,12 +1116,15 @@ function createFieldArrayManager(ctx, validate, setFocus) {
840
1116
  const clampedTo = Math.min(to, newItems.length);
841
1117
  newItems.splice(clampedTo, 0, removedItem);
842
1118
  fa.items.value = newItems;
843
- rebuildIndexCache();
1119
+ const minIdx = Math.min(from, clampedTo);
1120
+ const maxIdx = Math.max(from, clampedTo);
1121
+ const items = fa.items.value;
1122
+ for (let i = minIdx; i <= maxIdx; i++) {
1123
+ const item = items[i];
1124
+ if (item) indexCache.set(item.key, i);
1125
+ }
844
1126
  }
845
- ctx.dirtyFields.value = {
846
- ...ctx.dirtyFields.value,
847
- [name]: true
848
- };
1127
+ markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
849
1128
  if (ctx.options.mode === "onChange") validate(name);
850
1129
  return true;
851
1130
  };
@@ -854,10 +1133,7 @@ function createFieldArrayManager(ctx, validate, setFocus) {
854
1133
  set(ctx.formData, name, newValues);
855
1134
  fa.items.value = newValues.map(() => createItem(generateId()));
856
1135
  rebuildIndexCache();
857
- ctx.dirtyFields.value = {
858
- ...ctx.dirtyFields.value,
859
- [name]: true
860
- };
1136
+ markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
861
1137
  if (ctx.options.mode === "onChange") validate(name);
862
1138
  return true;
863
1139
  };
@@ -872,11 +1148,8 @@ function createFieldArrayManager(ctx, validate, setFocus) {
872
1148
  }
873
1149
  set(ctx.formData, name, []);
874
1150
  fa.items.value = [];
875
- rebuildIndexCache();
876
- ctx.dirtyFields.value = {
877
- ...ctx.dirtyFields.value,
878
- [name]: true
879
- };
1151
+ indexCache.clear();
1152
+ markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
880
1153
  if (ctx.options.mode === "onChange") validate(name);
881
1154
  return true;
882
1155
  };
@@ -895,14 +1168,15 @@ function createFieldArrayManager(ctx, validate, setFocus) {
895
1168
  }
896
1169
  const sortedIndices = [...new Set(validIndices)].sort((a, b) => b - a);
897
1170
  const indicesToRemove = new Set(sortedIndices);
1171
+ const keysToRemove = fa.items.value.filter((_, i) => indicesToRemove.has(i)).map((item) => item.key);
898
1172
  const newValues = currentValues.filter((_, i) => !indicesToRemove.has(i));
899
1173
  set(ctx.formData, name, newValues);
900
1174
  fa.items.value = fa.items.value.filter((_, i) => !indicesToRemove.has(i));
901
- rebuildIndexCache();
902
- ctx.dirtyFields.value = {
903
- ...ctx.dirtyFields.value,
904
- [name]: true
905
- };
1175
+ for (const key of keysToRemove) indexCache.delete(key);
1176
+ fa.items.value.forEach((item, idx) => {
1177
+ indexCache.set(item.key, idx);
1178
+ });
1179
+ markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
906
1180
  if (ctx.options.mode === "onChange") validate(name);
907
1181
  return true;
908
1182
  };
@@ -934,33 +1208,6 @@ function updateDomElement(el, value) {
934
1208
  if (el.type === "checkbox") el.checked = value;
935
1209
  else el.value = value;
936
1210
  }
937
- function markFieldDirty(dirtyFields, fieldName) {
938
- dirtyFields.value = {
939
- ...dirtyFields.value,
940
- [fieldName]: true
941
- };
942
- }
943
- function markFieldTouched(touchedFields, fieldName) {
944
- touchedFields.value = {
945
- ...touchedFields.value,
946
- [fieldName]: true
947
- };
948
- }
949
- function clearFieldDirty(dirtyFields, fieldName) {
950
- const newDirty = { ...dirtyFields.value };
951
- delete newDirty[fieldName];
952
- dirtyFields.value = newDirty;
953
- }
954
- function clearFieldTouched(touchedFields, fieldName) {
955
- const newTouched = { ...touchedFields.value };
956
- delete newTouched[fieldName];
957
- touchedFields.value = newTouched;
958
- }
959
- function clearFieldErrors(errors, fieldName) {
960
- const newErrors = { ...errors.value };
961
- for (const key of Object.keys(newErrors)) if (key === fieldName || key.startsWith(`${fieldName}.`)) delete newErrors[key];
962
- errors.value = newErrors;
963
- }
964
1211
  function useForm(options) {
965
1212
  const ctx = createFormContext(options);
966
1213
  const { validate, clearAllPendingErrors } = createValidation(ctx);
@@ -984,32 +1231,84 @@ function useForm(options) {
984
1231
  }
985
1232
  const setFocusWrapper = (name) => setFocus(name);
986
1233
  const { fields } = createFieldArrayManager(ctx, validate, setFocusWrapper);
1234
+ let lastSyncTime = 0;
1235
+ const SYNC_DEBOUNCE_MS = 16;
1236
+ function syncWithDebounce() {
1237
+ const now = typeof performance !== "undefined" ? performance.now() : Date.now();
1238
+ if (now - lastSyncTime < SYNC_DEBOUNCE_MS) return;
1239
+ syncUncontrolledInputs(ctx.fieldRefs, ctx.fieldOptions, ctx.formData);
1240
+ lastSyncTime = now;
1241
+ }
1242
+ let lastErrors = ctx.errors.value;
1243
+ let lastExternalErrors = ctx.externalErrors.value;
1244
+ let cachedMergedErrors = null;
987
1245
  function getMergedErrors() {
988
- return {
1246
+ if (cachedMergedErrors !== null && lastErrors === ctx.errors.value && lastExternalErrors === ctx.externalErrors.value) return cachedMergedErrors;
1247
+ lastErrors = ctx.errors.value;
1248
+ lastExternalErrors = ctx.externalErrors.value;
1249
+ cachedMergedErrors = {
989
1250
  ...ctx.errors.value,
990
1251
  ...ctx.externalErrors.value
991
1252
  };
1253
+ return cachedMergedErrors;
992
1254
  }
993
- const formState = (0, vue.computed)(() => {
994
- const mergedErrors = getMergedErrors();
995
- return {
996
- errors: mergedErrors,
997
- isDirty: Object.keys(ctx.dirtyFields.value).some((k) => ctx.dirtyFields.value[k]),
998
- dirtyFields: ctx.dirtyFields.value,
999
- isValid: (ctx.submitCount.value > 0 || Object.keys(ctx.touchedFields.value).length > 0) && Object.keys(mergedErrors).length === 0,
1000
- isSubmitting: ctx.isSubmitting.value,
1001
- isLoading: ctx.isLoading.value,
1002
- isReady: !ctx.isLoading.value,
1003
- isValidating: Object.keys(ctx.validatingFields.value).some((k) => ctx.validatingFields.value[k]),
1004
- validatingFields: ctx.validatingFields.value,
1005
- touchedFields: ctx.touchedFields.value,
1006
- submitCount: ctx.submitCount.value,
1007
- defaultValuesError: ctx.defaultValuesError.value,
1008
- isSubmitted: ctx.submitCount.value > 0,
1009
- isSubmitSuccessful: ctx.isSubmitSuccessful.value,
1010
- disabled: ctx.isDisabled.value
1011
- };
1255
+ const isDirtyComputed = (0, vue.computed)(() => ctx.dirtyFieldCount.value > 0);
1256
+ const errorsComputed = (0, vue.computed)(() => getMergedErrors());
1257
+ const isValidComputed = (0, vue.computed)(() => {
1258
+ if (!(ctx.submitCount.value > 0 || ctx.touchedFieldCount.value > 0)) return false;
1259
+ return Object.keys(errorsComputed.value).length === 0;
1012
1260
  });
1261
+ const isReadyComputed = (0, vue.computed)(() => !ctx.isLoading.value);
1262
+ const isValidatingComputed = (0, vue.computed)(() => ctx.validatingFields.value.size > 0);
1263
+ const isSubmittedComputed = (0, vue.computed)(() => ctx.submitCount.value > 0);
1264
+ const formStateInternal = (0, vue.reactive)({
1265
+ get errors() {
1266
+ return errorsComputed.value;
1267
+ },
1268
+ get isDirty() {
1269
+ return isDirtyComputed.value;
1270
+ },
1271
+ get dirtyFields() {
1272
+ return ctx.dirtyFields.value;
1273
+ },
1274
+ get isValid() {
1275
+ return isValidComputed.value;
1276
+ },
1277
+ get isSubmitting() {
1278
+ return ctx.isSubmitting.value;
1279
+ },
1280
+ get isLoading() {
1281
+ return ctx.isLoading.value;
1282
+ },
1283
+ get isReady() {
1284
+ return isReadyComputed.value;
1285
+ },
1286
+ get isValidating() {
1287
+ return isValidatingComputed.value;
1288
+ },
1289
+ get validatingFields() {
1290
+ return ctx.validatingFields.value;
1291
+ },
1292
+ get touchedFields() {
1293
+ return ctx.touchedFields.value;
1294
+ },
1295
+ get submitCount() {
1296
+ return ctx.submitCount.value;
1297
+ },
1298
+ get defaultValuesError() {
1299
+ return ctx.defaultValuesError.value;
1300
+ },
1301
+ get isSubmitted() {
1302
+ return isSubmittedComputed.value;
1303
+ },
1304
+ get isSubmitSuccessful() {
1305
+ return ctx.isSubmitSuccessful.value;
1306
+ },
1307
+ get disabled() {
1308
+ return ctx.isDisabled.value;
1309
+ }
1310
+ });
1311
+ const formState = (0, vue.computed)(() => formStateInternal);
1013
1312
  function handleSubmit(onValid, onInvalid) {
1014
1313
  return async (e) => {
1015
1314
  e.preventDefault();
@@ -1019,7 +1318,7 @@ function useForm(options) {
1019
1318
  ctx.submitCount.value++;
1020
1319
  ctx.isSubmitSuccessful.value = false;
1021
1320
  try {
1022
- syncUncontrolledInputs(ctx.fieldRefs, ctx.fieldOptions, ctx.formData);
1321
+ syncWithDebounce();
1023
1322
  if (await validate()) {
1024
1323
  await onValid(ctx.formData);
1025
1324
  ctx.isSubmitSuccessful.value = true;
@@ -1045,8 +1344,8 @@ function useForm(options) {
1045
1344
  }
1046
1345
  }
1047
1346
  set(ctx.formData, name, value);
1048
- if (setValueOptions?.shouldDirty !== false) markFieldDirty(ctx.dirtyFields, name);
1049
- if (setValueOptions?.shouldTouch) markFieldTouched(ctx.touchedFields, name);
1347
+ if (setValueOptions?.shouldDirty !== false) markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
1348
+ if (setValueOptions?.shouldTouch) markFieldTouched(ctx.touchedFields, ctx.touchedFieldCount, name);
1050
1349
  if (!ctx.fieldOptions.get(name)?.controlled) {
1051
1350
  const fieldRef = ctx.fieldRefs.get(name);
1052
1351
  if (fieldRef?.value) updateDomElement(fieldRef.value, value);
@@ -1057,15 +1356,23 @@ function useForm(options) {
1057
1356
  const opts = resetOptions || {};
1058
1357
  ctx.resetGeneration.value++;
1059
1358
  clearAllPendingErrors();
1060
- ctx.validatingFields.value = {};
1359
+ ctx.validatingFields.value = /* @__PURE__ */ new Set();
1360
+ ctx.validationCache.clear();
1361
+ for (const timer of ctx.schemaValidationTimers.values()) clearTimeout(timer);
1362
+ ctx.schemaValidationTimers.clear();
1061
1363
  if (!opts.keepDefaultValues && values) Object.assign(ctx.defaultValues, values);
1062
1364
  Object.keys(ctx.formData).forEach((key) => delete ctx.formData[key]);
1063
- const sourceValues = values || ctx.defaultValues;
1064
- const newValues = JSON.parse(JSON.stringify(sourceValues));
1365
+ const newValues = deepClone(values || ctx.defaultValues);
1065
1366
  Object.assign(ctx.formData, newValues);
1066
1367
  if (!opts.keepErrors) ctx.errors.value = {};
1067
- if (!opts.keepTouched) ctx.touchedFields.value = {};
1068
- if (!opts.keepDirty) ctx.dirtyFields.value = {};
1368
+ if (!opts.keepTouched) {
1369
+ ctx.touchedFields.value = {};
1370
+ ctx.touchedFieldCount.value = 0;
1371
+ }
1372
+ if (!opts.keepDirty) {
1373
+ ctx.dirtyFields.value = {};
1374
+ ctx.dirtyFieldCount.value = 0;
1375
+ }
1069
1376
  if (!opts.keepSubmitCount) ctx.submitCount.value = 0;
1070
1377
  if (!opts.keepIsSubmitting) ctx.isSubmitting.value = false;
1071
1378
  if (!opts.keepIsSubmitSuccessful) ctx.isSubmitSuccessful.value = false;
@@ -1099,11 +1406,11 @@ function useForm(options) {
1099
1406
  let defaultValue = opts.defaultValue;
1100
1407
  if (defaultValue === void 0) defaultValue = get(ctx.defaultValues, name);
1101
1408
  else set(ctx.defaultValues, name, defaultValue);
1102
- const clonedValue = defaultValue !== void 0 ? JSON.parse(JSON.stringify(defaultValue)) : void 0;
1409
+ const clonedValue = defaultValue !== void 0 ? deepClone(defaultValue) : void 0;
1103
1410
  set(ctx.formData, name, clonedValue);
1104
1411
  if (!opts.keepError) clearFieldErrors(ctx.errors, name);
1105
- if (!opts.keepDirty) clearFieldDirty(ctx.dirtyFields, name);
1106
- if (!opts.keepTouched) clearFieldTouched(ctx.touchedFields, name);
1412
+ if (!opts.keepDirty) clearFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
1413
+ if (!opts.keepTouched) clearFieldTouched(ctx.touchedFields, ctx.touchedFieldCount, name);
1107
1414
  if (!ctx.fieldOptions.get(name)?.controlled) {
1108
1415
  const fieldRef = ctx.fieldRefs.get(name);
1109
1416
  if (fieldRef?.value) updateDomElement(fieldRef.value, clonedValue ?? (fieldRef.value.type === "checkbox" ? false : ""));
@@ -1193,7 +1500,7 @@ function useForm(options) {
1193
1500
  }
1194
1501
  }
1195
1502
  }
1196
- syncUncontrolledInputs(ctx.fieldRefs, ctx.fieldOptions, ctx.formData);
1503
+ syncWithDebounce();
1197
1504
  if (nameOrNames === void 0) return { ...ctx.formData };
1198
1505
  if (Array.isArray(nameOrNames)) {
1199
1506
  const result = {};
@@ -1300,7 +1607,12 @@ function useController(options) {
1300
1607
  form.setValue(name, newValue);
1301
1608
  };
1302
1609
  const onBlur = () => {
1303
- form.trigger(name);
1610
+ const currentValue = form.getValues(name);
1611
+ form.setValue(name, currentValue, {
1612
+ shouldTouch: true,
1613
+ shouldValidate: true,
1614
+ shouldDirty: false
1615
+ });
1304
1616
  };
1305
1617
  const refCallback = (el) => {
1306
1618
  elementRef.value = el;