@vuehookform/core 0.3.3 → 0.4.0

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,108 @@ 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) return cached.isValid;
628
+ const analysis = analyzeSchemaPath(ctx.options.schema, fieldPath);
629
+ if (analysis.canPartialValidate && analysis.subSchema) return validateFieldPartial(fieldPath, analysis.subSchema, valueHash, criteriaMode, generationAtStart);
630
+ return validateFieldFull(fieldPath, valueHash, criteriaMode, generationAtStart);
631
+ }
632
+ const validatingKey = "_form";
403
633
  setValidating(ctx, validatingKey, true);
404
634
  try {
405
635
  const result = await ctx.options.schema.safeParseAsync(ctx.formData);
406
636
  if (ctx.resetGeneration.value !== generationAtStart) return true;
407
637
  if (result.success) {
408
- if (fieldPath) ctx.errors.value = cancelError(fieldPath);
409
- else {
410
- clearAllPendingErrors();
411
- ctx.errors.value = {};
412
- clearAllNativeValidation();
413
- }
638
+ clearAllPendingErrors();
639
+ ctx.errors.value = {};
640
+ clearAllNativeValidation();
641
+ ctx.validationCache.clear();
414
642
  return true;
415
643
  }
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
644
  clearAllPendingErrors();
432
645
  ctx.errors.value = {};
433
- const grouped = groupErrorsByPath(zodErrors);
434
- for (const [path, errors] of grouped) scheduleError(path, createFieldError(errors, criteriaMode));
646
+ const grouped = groupErrorsByPath(result.error.issues);
647
+ const errorBatch = [];
648
+ for (const [path, errors] of grouped) errorBatch.push([path, createFieldError(errors, criteriaMode)]);
649
+ scheduleErrorsBatch(errorBatch);
650
+ ctx.validationCache.clear();
435
651
  return false;
436
652
  } finally {
437
653
  setValidating(ctx, validatingKey, false);
@@ -442,6 +658,48 @@ function createValidation(ctx) {
442
658
  clearAllPendingErrors
443
659
  };
444
660
  }
661
+ function markFieldDirty(dirtyFields, dirtyFieldCount, fieldName) {
662
+ if (dirtyFields.value[fieldName]) return;
663
+ dirtyFields.value = {
664
+ ...dirtyFields.value,
665
+ [fieldName]: true
666
+ };
667
+ dirtyFieldCount.value++;
668
+ }
669
+ function markFieldTouched(touchedFields, touchedFieldCount, fieldName) {
670
+ if (touchedFields.value[fieldName]) return;
671
+ touchedFields.value = {
672
+ ...touchedFields.value,
673
+ [fieldName]: true
674
+ };
675
+ touchedFieldCount.value++;
676
+ }
677
+ function clearFieldDirty(dirtyFields, dirtyFieldCount, fieldName) {
678
+ if (!(fieldName in dirtyFields.value)) return;
679
+ const newDirty = { ...dirtyFields.value };
680
+ delete newDirty[fieldName];
681
+ dirtyFields.value = newDirty;
682
+ dirtyFieldCount.value--;
683
+ }
684
+ function clearFieldTouched(touchedFields, touchedFieldCount, fieldName) {
685
+ if (!(fieldName in touchedFields.value)) return;
686
+ const newTouched = { ...touchedFields.value };
687
+ delete newTouched[fieldName];
688
+ touchedFields.value = newTouched;
689
+ touchedFieldCount.value--;
690
+ }
691
+ function clearFieldErrors(errors, fieldName) {
692
+ const currentErrors = errors.value;
693
+ const keys = Object.keys(currentErrors);
694
+ if (keys.length === 0) return;
695
+ const prefix = `${fieldName}.`;
696
+ const keysToDelete = [];
697
+ for (const key of keys) if (key === fieldName || key.startsWith(prefix)) keysToDelete.push(key);
698
+ if (keysToDelete.length === 0) return;
699
+ const newErrors = { ...currentErrors };
700
+ for (const key of keysToDelete) delete newErrors[key];
701
+ errors.value = newErrors;
702
+ }
445
703
  var validationRequestCounter = 0;
446
704
  function createFieldRegistration(ctx, validate) {
447
705
  function register(name, registerOptions) {
@@ -483,16 +741,30 @@ function createFieldRegistration(ctx, validate) {
483
741
  const target = e.target;
484
742
  const value = target.type === "checkbox" ? target.checked : target.value;
485
743
  set(ctx.formData, name, value);
486
- ctx.dirtyFields.value = {
487
- ...ctx.dirtyFields.value,
488
- [name]: true
489
- };
744
+ markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
745
+ const fieldOpts = ctx.fieldOptions.get(name);
490
746
  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);
747
+ const validationDebounceMs = ctx.options.validationDebounce || 0;
748
+ if (validationDebounceMs > 0) {
749
+ const existingTimer = ctx.schemaValidationTimers.get(name);
750
+ if (existingTimer) clearTimeout(existingTimer);
751
+ const timer = setTimeout(async () => {
752
+ ctx.schemaValidationTimers.delete(name);
753
+ await validate(name);
754
+ if (fieldOpts?.deps && fieldOpts.deps.length > 0) {
755
+ const uniqueDeps = [...new Set(fieldOpts.deps)];
756
+ await Promise.all(uniqueDeps.map((depField) => validate(depField)));
757
+ }
758
+ }, validationDebounceMs);
759
+ ctx.schemaValidationTimers.set(name, timer);
760
+ } else {
761
+ await validate(name);
762
+ if (fieldOpts?.deps && fieldOpts.deps.length > 0) {
763
+ const uniqueDeps = [...new Set(fieldOpts.deps)];
764
+ await Promise.all(uniqueDeps.map((depField) => validate(depField)));
765
+ }
766
+ }
494
767
  }
495
- const fieldOpts = ctx.fieldOptions.get(name);
496
768
  if (fieldOpts?.validate && !fieldOpts.disabled) {
497
769
  const requestId = ++validationRequestCounter;
498
770
  ctx.validationRequestIds.set(name, requestId);
@@ -501,23 +773,24 @@ function createFieldRegistration(ctx, validate) {
501
773
  if (debounceMs > 0) {
502
774
  const existingTimer = ctx.debounceTimers.get(name);
503
775
  if (existingTimer) clearTimeout(existingTimer);
504
- const timer = setTimeout(() => {
776
+ const timer = setTimeout(async () => {
505
777
  ctx.debounceTimers.delete(name);
506
- runCustomValidation(name, value, requestId, resetGenAtStart);
778
+ await runCustomValidation(name, value, requestId, resetGenAtStart);
779
+ ctx.validationRequestIds.delete(name);
507
780
  }, debounceMs);
508
781
  ctx.debounceTimers.set(name, timer);
509
782
  } else await runCustomValidation(name, value, requestId, resetGenAtStart);
510
783
  }
511
784
  };
512
785
  const onBlur = async (_e) => {
513
- ctx.touchedFields.value = {
514
- ...ctx.touchedFields.value,
515
- [name]: true
516
- };
786
+ markFieldTouched(ctx.touchedFields, ctx.touchedFieldCount, name);
517
787
  if (ctx.options.mode === "onBlur" || ctx.options.mode === "onTouched" || ctx.submitCount.value > 0 && ctx.options.reValidateMode === "onBlur") {
518
788
  await validate(name);
519
789
  const fieldOpts = ctx.fieldOptions.get(name);
520
- if (fieldOpts?.deps && fieldOpts.deps.length > 0) for (const depField of fieldOpts.deps) validate(depField);
790
+ if (fieldOpts?.deps && fieldOpts.deps.length > 0) {
791
+ const uniqueDeps = [...new Set(fieldOpts.deps)];
792
+ await Promise.all(uniqueDeps.map((depField) => validate(depField)));
793
+ }
521
794
  }
522
795
  };
523
796
  const refCallback = (el) => {
@@ -539,18 +812,17 @@ function createFieldRegistration(ctx, validate) {
539
812
  clearTimeout(timer);
540
813
  ctx.debounceTimers.delete(name);
541
814
  }
815
+ const schemaTimer = ctx.schemaValidationTimers.get(name);
816
+ if (schemaTimer) {
817
+ clearTimeout(schemaTimer);
818
+ ctx.schemaValidationTimers.delete(name);
819
+ }
542
820
  ctx.validationRequestIds.delete(name);
543
821
  if (opts?.shouldUnregister ?? ctx.options.shouldUnregister ?? false) {
544
822
  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;
823
+ clearFieldErrors(ctx.errors, name);
824
+ clearFieldTouched(ctx.touchedFields, ctx.touchedFieldCount, name);
825
+ clearFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
554
826
  ctx.fieldRefs.delete(name);
555
827
  ctx.fieldOptions.delete(name);
556
828
  ctx.fieldHandlers.delete(name);
@@ -574,10 +846,7 @@ function createFieldRegistration(ctx, validate) {
574
846
  get: () => get(ctx.formData, name),
575
847
  set: (val) => {
576
848
  set(ctx.formData, name, val);
577
- ctx.dirtyFields.value = {
578
- ...ctx.dirtyFields.value,
579
- [name]: true
580
- };
849
+ markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
581
850
  }
582
851
  }) }
583
852
  };
@@ -585,21 +854,9 @@ function createFieldRegistration(ctx, validate) {
585
854
  function unregister(name, options) {
586
855
  const opts = options || {};
587
856
  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
- }
857
+ if (!opts.keepError) clearFieldErrors(ctx.errors, name);
858
+ if (!opts.keepTouched) clearFieldTouched(ctx.touchedFields, ctx.touchedFieldCount, name);
859
+ if (!opts.keepDirty) clearFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
603
860
  ctx.fieldRefs.delete(name);
604
861
  ctx.fieldOptions.delete(name);
605
862
  ctx.fieldHandlers.delete(name);
@@ -608,6 +865,11 @@ function createFieldRegistration(ctx, validate) {
608
865
  clearTimeout(timer);
609
866
  ctx.debounceTimers.delete(name);
610
867
  }
868
+ const schemaTimer = ctx.schemaValidationTimers.get(name);
869
+ if (schemaTimer) {
870
+ clearTimeout(schemaTimer);
871
+ ctx.schemaValidationTimers.delete(name);
872
+ }
611
873
  ctx.validationRequestIds.delete(name);
612
874
  }
613
875
  return {
@@ -644,6 +906,35 @@ function createFieldArrayManager(ctx, validate, setFocus) {
644
906
  indexCache.set(item.key, idx);
645
907
  });
646
908
  };
909
+ const appendToCache = (startIndex) => {
910
+ const items = fa.items.value;
911
+ for (let i = startIndex; i < items.length; i++) {
912
+ const item = items[i];
913
+ if (item) indexCache.set(item.key, i);
914
+ }
915
+ };
916
+ const updateCacheAfterInsert = (insertIndex, _insertCount) => {
917
+ const items = fa.items.value;
918
+ for (let i = insertIndex; i < items.length; i++) {
919
+ const item = items[i];
920
+ if (item) indexCache.set(item.key, i);
921
+ }
922
+ };
923
+ const swapInCache = (indexA, indexB) => {
924
+ const items = fa.items.value;
925
+ const itemA = items[indexA];
926
+ const itemB = items[indexB];
927
+ if (itemA) indexCache.set(itemA.key, indexA);
928
+ if (itemB) indexCache.set(itemB.key, indexB);
929
+ };
930
+ const updateCacheAfterRemove = (removedKey, startIndex) => {
931
+ indexCache.delete(removedKey);
932
+ const items = fa.items.value;
933
+ for (let i = startIndex; i < items.length; i++) {
934
+ const item = items[i];
935
+ if (item) indexCache.set(item.key, i);
936
+ }
937
+ };
647
938
  const createItem = (key) => ({
648
939
  key,
649
940
  get index() {
@@ -686,11 +977,8 @@ function createFieldArrayManager(ctx, validate, setFocus) {
686
977
  set(ctx.formData, name, newValues);
687
978
  const newItems = values.map(() => createItem(generateId()));
688
979
  fa.items.value = [...fa.items.value, ...newItems];
689
- rebuildIndexCache();
690
- ctx.dirtyFields.value = {
691
- ...ctx.dirtyFields.value,
692
- [name]: true
693
- };
980
+ appendToCache(insertIndex);
981
+ markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
694
982
  if (ctx.options.mode === "onChange") validate(name);
695
983
  handleFocus(insertIndex, values.length, focusOptions);
696
984
  return true;
@@ -711,11 +999,8 @@ function createFieldArrayManager(ctx, validate, setFocus) {
711
999
  set(ctx.formData, name, newValues);
712
1000
  const newItems = values.map(() => createItem(generateId()));
713
1001
  fa.items.value = [...newItems, ...fa.items.value];
714
- rebuildIndexCache();
715
- ctx.dirtyFields.value = {
716
- ...ctx.dirtyFields.value,
717
- [name]: true
718
- };
1002
+ updateCacheAfterInsert(0, values.length);
1003
+ markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
719
1004
  if (ctx.options.mode === "onChange") validate(name);
720
1005
  handleFocus(0, values.length, focusOptions);
721
1006
  return true;
@@ -729,10 +1014,7 @@ function createFieldArrayManager(ctx, validate, setFocus) {
729
1014
  const newValues = [...currentValues];
730
1015
  newValues[index] = value;
731
1016
  set(ctx.formData, name, newValues);
732
- ctx.dirtyFields.value = {
733
- ...ctx.dirtyFields.value,
734
- [name]: true
735
- };
1017
+ markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
736
1018
  if (ctx.options.mode === "onChange") validate(name);
737
1019
  return true;
738
1020
  };
@@ -754,11 +1036,8 @@ function createFieldArrayManager(ctx, validate, setFocus) {
754
1036
  set(ctx.formData, name, newValues);
755
1037
  const keyToRemove = fa.items.value[index]?.key;
756
1038
  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
- };
1039
+ if (keyToRemove) updateCacheAfterRemove(keyToRemove, index);
1040
+ markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
762
1041
  if (ctx.options.mode === "onChange") validate(name);
763
1042
  return true;
764
1043
  };
@@ -787,11 +1066,8 @@ function createFieldArrayManager(ctx, validate, setFocus) {
787
1066
  ...newItems,
788
1067
  ...fa.items.value.slice(clampedIndex)
789
1068
  ];
790
- rebuildIndexCache();
791
- ctx.dirtyFields.value = {
792
- ...ctx.dirtyFields.value,
793
- [name]: true
794
- };
1069
+ updateCacheAfterInsert(clampedIndex, values.length);
1070
+ markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
795
1071
  if (ctx.options.mode === "onChange") validate(name);
796
1072
  handleFocus(clampedIndex, values.length, focusOptions);
797
1073
  return true;
@@ -812,12 +1088,9 @@ function createFieldArrayManager(ctx, validate, setFocus) {
812
1088
  newItems[indexA] = itemB;
813
1089
  newItems[indexB] = itemA;
814
1090
  fa.items.value = newItems;
815
- rebuildIndexCache();
1091
+ swapInCache(indexA, indexB);
816
1092
  }
817
- ctx.dirtyFields.value = {
818
- ...ctx.dirtyFields.value,
819
- [name]: true
820
- };
1093
+ markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
821
1094
  if (ctx.options.mode === "onChange") validate(name);
822
1095
  return true;
823
1096
  };
@@ -840,12 +1113,15 @@ function createFieldArrayManager(ctx, validate, setFocus) {
840
1113
  const clampedTo = Math.min(to, newItems.length);
841
1114
  newItems.splice(clampedTo, 0, removedItem);
842
1115
  fa.items.value = newItems;
843
- rebuildIndexCache();
1116
+ const minIdx = Math.min(from, clampedTo);
1117
+ const maxIdx = Math.max(from, clampedTo);
1118
+ const items = fa.items.value;
1119
+ for (let i = minIdx; i <= maxIdx; i++) {
1120
+ const item = items[i];
1121
+ if (item) indexCache.set(item.key, i);
1122
+ }
844
1123
  }
845
- ctx.dirtyFields.value = {
846
- ...ctx.dirtyFields.value,
847
- [name]: true
848
- };
1124
+ markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
849
1125
  if (ctx.options.mode === "onChange") validate(name);
850
1126
  return true;
851
1127
  };
@@ -854,10 +1130,7 @@ function createFieldArrayManager(ctx, validate, setFocus) {
854
1130
  set(ctx.formData, name, newValues);
855
1131
  fa.items.value = newValues.map(() => createItem(generateId()));
856
1132
  rebuildIndexCache();
857
- ctx.dirtyFields.value = {
858
- ...ctx.dirtyFields.value,
859
- [name]: true
860
- };
1133
+ markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
861
1134
  if (ctx.options.mode === "onChange") validate(name);
862
1135
  return true;
863
1136
  };
@@ -872,11 +1145,8 @@ function createFieldArrayManager(ctx, validate, setFocus) {
872
1145
  }
873
1146
  set(ctx.formData, name, []);
874
1147
  fa.items.value = [];
875
- rebuildIndexCache();
876
- ctx.dirtyFields.value = {
877
- ...ctx.dirtyFields.value,
878
- [name]: true
879
- };
1148
+ indexCache.clear();
1149
+ markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
880
1150
  if (ctx.options.mode === "onChange") validate(name);
881
1151
  return true;
882
1152
  };
@@ -895,14 +1165,15 @@ function createFieldArrayManager(ctx, validate, setFocus) {
895
1165
  }
896
1166
  const sortedIndices = [...new Set(validIndices)].sort((a, b) => b - a);
897
1167
  const indicesToRemove = new Set(sortedIndices);
1168
+ const keysToRemove = fa.items.value.filter((_, i) => indicesToRemove.has(i)).map((item) => item.key);
898
1169
  const newValues = currentValues.filter((_, i) => !indicesToRemove.has(i));
899
1170
  set(ctx.formData, name, newValues);
900
1171
  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
- };
1172
+ for (const key of keysToRemove) indexCache.delete(key);
1173
+ fa.items.value.forEach((item, idx) => {
1174
+ indexCache.set(item.key, idx);
1175
+ });
1176
+ markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
906
1177
  if (ctx.options.mode === "onChange") validate(name);
907
1178
  return true;
908
1179
  };
@@ -934,33 +1205,6 @@ function updateDomElement(el, value) {
934
1205
  if (el.type === "checkbox") el.checked = value;
935
1206
  else el.value = value;
936
1207
  }
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
1208
  function useForm(options) {
965
1209
  const ctx = createFormContext(options);
966
1210
  const { validate, clearAllPendingErrors } = createValidation(ctx);
@@ -984,32 +1228,84 @@ function useForm(options) {
984
1228
  }
985
1229
  const setFocusWrapper = (name) => setFocus(name);
986
1230
  const { fields } = createFieldArrayManager(ctx, validate, setFocusWrapper);
1231
+ let lastSyncTime = 0;
1232
+ const SYNC_DEBOUNCE_MS = 16;
1233
+ function syncWithDebounce() {
1234
+ const now = typeof performance !== "undefined" ? performance.now() : Date.now();
1235
+ if (now - lastSyncTime < SYNC_DEBOUNCE_MS) return;
1236
+ syncUncontrolledInputs(ctx.fieldRefs, ctx.fieldOptions, ctx.formData);
1237
+ lastSyncTime = now;
1238
+ }
1239
+ let lastErrors = ctx.errors.value;
1240
+ let lastExternalErrors = ctx.externalErrors.value;
1241
+ let cachedMergedErrors = null;
987
1242
  function getMergedErrors() {
988
- return {
1243
+ if (cachedMergedErrors !== null && lastErrors === ctx.errors.value && lastExternalErrors === ctx.externalErrors.value) return cachedMergedErrors;
1244
+ lastErrors = ctx.errors.value;
1245
+ lastExternalErrors = ctx.externalErrors.value;
1246
+ cachedMergedErrors = {
989
1247
  ...ctx.errors.value,
990
1248
  ...ctx.externalErrors.value
991
1249
  };
1250
+ return cachedMergedErrors;
992
1251
  }
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
- };
1252
+ const isDirtyComputed = (0, vue.computed)(() => ctx.dirtyFieldCount.value > 0);
1253
+ const errorsComputed = (0, vue.computed)(() => getMergedErrors());
1254
+ const isValidComputed = (0, vue.computed)(() => {
1255
+ if (!(ctx.submitCount.value > 0 || ctx.touchedFieldCount.value > 0)) return false;
1256
+ return Object.keys(errorsComputed.value).length === 0;
1012
1257
  });
1258
+ const isReadyComputed = (0, vue.computed)(() => !ctx.isLoading.value);
1259
+ const isValidatingComputed = (0, vue.computed)(() => ctx.validatingFields.value.size > 0);
1260
+ const isSubmittedComputed = (0, vue.computed)(() => ctx.submitCount.value > 0);
1261
+ const formStateInternal = (0, vue.reactive)({
1262
+ get errors() {
1263
+ return errorsComputed.value;
1264
+ },
1265
+ get isDirty() {
1266
+ return isDirtyComputed.value;
1267
+ },
1268
+ get dirtyFields() {
1269
+ return ctx.dirtyFields.value;
1270
+ },
1271
+ get isValid() {
1272
+ return isValidComputed.value;
1273
+ },
1274
+ get isSubmitting() {
1275
+ return ctx.isSubmitting.value;
1276
+ },
1277
+ get isLoading() {
1278
+ return ctx.isLoading.value;
1279
+ },
1280
+ get isReady() {
1281
+ return isReadyComputed.value;
1282
+ },
1283
+ get isValidating() {
1284
+ return isValidatingComputed.value;
1285
+ },
1286
+ get validatingFields() {
1287
+ return ctx.validatingFields.value;
1288
+ },
1289
+ get touchedFields() {
1290
+ return ctx.touchedFields.value;
1291
+ },
1292
+ get submitCount() {
1293
+ return ctx.submitCount.value;
1294
+ },
1295
+ get defaultValuesError() {
1296
+ return ctx.defaultValuesError.value;
1297
+ },
1298
+ get isSubmitted() {
1299
+ return isSubmittedComputed.value;
1300
+ },
1301
+ get isSubmitSuccessful() {
1302
+ return ctx.isSubmitSuccessful.value;
1303
+ },
1304
+ get disabled() {
1305
+ return ctx.isDisabled.value;
1306
+ }
1307
+ });
1308
+ const formState = (0, vue.computed)(() => formStateInternal);
1013
1309
  function handleSubmit(onValid, onInvalid) {
1014
1310
  return async (e) => {
1015
1311
  e.preventDefault();
@@ -1019,7 +1315,7 @@ function useForm(options) {
1019
1315
  ctx.submitCount.value++;
1020
1316
  ctx.isSubmitSuccessful.value = false;
1021
1317
  try {
1022
- syncUncontrolledInputs(ctx.fieldRefs, ctx.fieldOptions, ctx.formData);
1318
+ syncWithDebounce();
1023
1319
  if (await validate()) {
1024
1320
  await onValid(ctx.formData);
1025
1321
  ctx.isSubmitSuccessful.value = true;
@@ -1045,8 +1341,8 @@ function useForm(options) {
1045
1341
  }
1046
1342
  }
1047
1343
  set(ctx.formData, name, value);
1048
- if (setValueOptions?.shouldDirty !== false) markFieldDirty(ctx.dirtyFields, name);
1049
- if (setValueOptions?.shouldTouch) markFieldTouched(ctx.touchedFields, name);
1344
+ if (setValueOptions?.shouldDirty !== false) markFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
1345
+ if (setValueOptions?.shouldTouch) markFieldTouched(ctx.touchedFields, ctx.touchedFieldCount, name);
1050
1346
  if (!ctx.fieldOptions.get(name)?.controlled) {
1051
1347
  const fieldRef = ctx.fieldRefs.get(name);
1052
1348
  if (fieldRef?.value) updateDomElement(fieldRef.value, value);
@@ -1057,15 +1353,23 @@ function useForm(options) {
1057
1353
  const opts = resetOptions || {};
1058
1354
  ctx.resetGeneration.value++;
1059
1355
  clearAllPendingErrors();
1060
- ctx.validatingFields.value = {};
1356
+ ctx.validatingFields.value = /* @__PURE__ */ new Set();
1357
+ ctx.validationCache.clear();
1358
+ for (const timer of ctx.schemaValidationTimers.values()) clearTimeout(timer);
1359
+ ctx.schemaValidationTimers.clear();
1061
1360
  if (!opts.keepDefaultValues && values) Object.assign(ctx.defaultValues, values);
1062
1361
  Object.keys(ctx.formData).forEach((key) => delete ctx.formData[key]);
1063
- const sourceValues = values || ctx.defaultValues;
1064
- const newValues = JSON.parse(JSON.stringify(sourceValues));
1362
+ const newValues = deepClone(values || ctx.defaultValues);
1065
1363
  Object.assign(ctx.formData, newValues);
1066
1364
  if (!opts.keepErrors) ctx.errors.value = {};
1067
- if (!opts.keepTouched) ctx.touchedFields.value = {};
1068
- if (!opts.keepDirty) ctx.dirtyFields.value = {};
1365
+ if (!opts.keepTouched) {
1366
+ ctx.touchedFields.value = {};
1367
+ ctx.touchedFieldCount.value = 0;
1368
+ }
1369
+ if (!opts.keepDirty) {
1370
+ ctx.dirtyFields.value = {};
1371
+ ctx.dirtyFieldCount.value = 0;
1372
+ }
1069
1373
  if (!opts.keepSubmitCount) ctx.submitCount.value = 0;
1070
1374
  if (!opts.keepIsSubmitting) ctx.isSubmitting.value = false;
1071
1375
  if (!opts.keepIsSubmitSuccessful) ctx.isSubmitSuccessful.value = false;
@@ -1099,11 +1403,11 @@ function useForm(options) {
1099
1403
  let defaultValue = opts.defaultValue;
1100
1404
  if (defaultValue === void 0) defaultValue = get(ctx.defaultValues, name);
1101
1405
  else set(ctx.defaultValues, name, defaultValue);
1102
- const clonedValue = defaultValue !== void 0 ? JSON.parse(JSON.stringify(defaultValue)) : void 0;
1406
+ const clonedValue = defaultValue !== void 0 ? deepClone(defaultValue) : void 0;
1103
1407
  set(ctx.formData, name, clonedValue);
1104
1408
  if (!opts.keepError) clearFieldErrors(ctx.errors, name);
1105
- if (!opts.keepDirty) clearFieldDirty(ctx.dirtyFields, name);
1106
- if (!opts.keepTouched) clearFieldTouched(ctx.touchedFields, name);
1409
+ if (!opts.keepDirty) clearFieldDirty(ctx.dirtyFields, ctx.dirtyFieldCount, name);
1410
+ if (!opts.keepTouched) clearFieldTouched(ctx.touchedFields, ctx.touchedFieldCount, name);
1107
1411
  if (!ctx.fieldOptions.get(name)?.controlled) {
1108
1412
  const fieldRef = ctx.fieldRefs.get(name);
1109
1413
  if (fieldRef?.value) updateDomElement(fieldRef.value, clonedValue ?? (fieldRef.value.type === "checkbox" ? false : ""));
@@ -1193,7 +1497,7 @@ function useForm(options) {
1193
1497
  }
1194
1498
  }
1195
1499
  }
1196
- syncUncontrolledInputs(ctx.fieldRefs, ctx.fieldOptions, ctx.formData);
1500
+ syncWithDebounce();
1197
1501
  if (nameOrNames === void 0) return { ...ctx.formData };
1198
1502
  if (Array.isArray(nameOrNames)) {
1199
1503
  const result = {};