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