react-f0rm 1.1.1 → 1.2.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.
Files changed (61) hide show
  1. package/README.md +152 -32
  2. package/dist/devtools/index.cjs.js +1 -1
  3. package/dist/devtools/index.cjs.js.map +1 -1
  4. package/dist/devtools/index.d.ts +2 -2
  5. package/dist/devtools/index.mjs +1 -1
  6. package/dist/devtools/index.mjs.map +1 -1
  7. package/dist/errors-CxSjrWJO.cjs.js +2 -0
  8. package/dist/errors-CxSjrWJO.cjs.js.map +1 -0
  9. package/dist/errors-CzWtwjO0.mjs +2 -0
  10. package/dist/errors-CzWtwjO0.mjs.map +1 -0
  11. package/dist/form-CvmWHUrd.d.ts +423 -0
  12. package/dist/index.cjs.js +1 -1
  13. package/dist/index.cjs.js.map +1 -1
  14. package/dist/index.d.ts +786 -102
  15. package/dist/index.mjs +1 -1
  16. package/dist/index.mjs.map +1 -1
  17. package/dist/index.umd.js +796 -354
  18. package/dist/index.umd.js.map +1 -1
  19. package/dist/index.umd.min.js +2 -2
  20. package/dist/index.umd.min.js.map +1 -1
  21. package/dist/persist.cjs.js +2 -0
  22. package/dist/persist.cjs.js.map +1 -0
  23. package/dist/persist.d.ts +49 -0
  24. package/dist/persist.mjs +2 -0
  25. package/dist/persist.mjs.map +1 -0
  26. package/dist/resolvers/standard-schema.cjs.js +1 -1
  27. package/dist/resolvers/standard-schema.cjs.js.map +1 -1
  28. package/dist/resolvers/standard-schema.d.ts +7 -5
  29. package/dist/resolvers/standard-schema.mjs +1 -1
  30. package/dist/resolvers/standard-schema.mjs.map +1 -1
  31. package/dist/resolvers/yup.cjs.js +1 -1
  32. package/dist/resolvers/yup.cjs.js.map +1 -1
  33. package/dist/resolvers/yup.d.ts +1 -1
  34. package/dist/resolvers/yup.mjs +1 -1
  35. package/dist/resolvers/yup.mjs.map +1 -1
  36. package/dist/resolvers/zod.cjs.js +1 -1
  37. package/dist/resolvers/zod.cjs.js.map +1 -1
  38. package/dist/resolvers/zod.d.ts +1 -1
  39. package/dist/resolvers/zod.mjs +1 -1
  40. package/dist/resolvers/zod.mjs.map +1 -1
  41. package/dist/server/index.cjs.js +2 -0
  42. package/dist/server/index.cjs.js.map +1 -0
  43. package/dist/server/index.d.ts +77 -0
  44. package/dist/server/index.mjs +2 -0
  45. package/dist/server/index.mjs.map +1 -0
  46. package/dist/validate-B1Gdjeaq.mjs +2 -0
  47. package/dist/validate-B1Gdjeaq.mjs.map +1 -0
  48. package/dist/validate-CUmNZqg6.d.ts +238 -0
  49. package/dist/validate-DAfz8Nbb.cjs.js +2 -0
  50. package/dist/validate-DAfz8Nbb.cjs.js.map +1 -0
  51. package/dist/values-B1IV-6V4.mjs +2 -0
  52. package/dist/values-B1IV-6V4.mjs.map +1 -0
  53. package/dist/values-CDNAYEOB.cjs.js +2 -0
  54. package/dist/values-CDNAYEOB.cjs.js.map +1 -0
  55. package/package.json +71 -24
  56. package/dist/form-2_tBkEXU.mjs +0 -2
  57. package/dist/form-2_tBkEXU.mjs.map +0 -1
  58. package/dist/form-BiDaJLjD.d.ts +0 -826
  59. package/dist/form-BwLNQ6WB.cjs.js +0 -2
  60. package/dist/form-BwLNQ6WB.cjs.js.map +0 -1
  61. package/dist/validate-2XUilILy.d.ts +0 -22
package/dist/index.umd.js CHANGED
@@ -23,6 +23,67 @@
23
23
 
24
24
  var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
25
25
 
26
+ function _arrayLikeToArray(r, a) {
27
+ (null == a || a > r.length) && (a = r.length);
28
+ for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
29
+ return n;
30
+ }
31
+ function _createForOfIteratorHelper(r, e) {
32
+ var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
33
+ if (!t) {
34
+ if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e) {
35
+ t && (r = t);
36
+ var n = 0,
37
+ F = function () {};
38
+ return {
39
+ s: F,
40
+ n: function () {
41
+ return n >= r.length ? {
42
+ done: true
43
+ } : {
44
+ done: false,
45
+ value: r[n++]
46
+ };
47
+ },
48
+ e: function (r) {
49
+ throw r;
50
+ },
51
+ f: F
52
+ };
53
+ }
54
+ throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
55
+ }
56
+ var o,
57
+ a = true,
58
+ u = false;
59
+ return {
60
+ s: function () {
61
+ t = t.call(r);
62
+ },
63
+ n: function () {
64
+ var r = t.next();
65
+ return a = r.done, r;
66
+ },
67
+ e: function (r) {
68
+ u = true, o = r;
69
+ },
70
+ f: function () {
71
+ try {
72
+ a || null == t.return || t.return();
73
+ } finally {
74
+ if (u) throw o;
75
+ }
76
+ }
77
+ };
78
+ }
79
+ function _unsupportedIterableToArray(r, a) {
80
+ if (r) {
81
+ if ("string" == typeof r) return _arrayLikeToArray(r, a);
82
+ var t = {}.toString.call(r).slice(8, -1);
83
+ return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
84
+ }
85
+ }
86
+
26
87
  function getSet(ee, key) {
27
88
  var set = ee.get(key);
28
89
  if (set) return set;
@@ -30,20 +91,85 @@
30
91
  ee.set(key, newSet);
31
92
  return newSet;
32
93
  }
94
+
95
+ // DEV-only listener-leak warning. Checked via process.env.NODE_ENV because the
96
+ // build pipeline has no compile-time constant injection; the typeof guard
97
+ // keeps direct browser usage of the UMD build safe. In production the whole
98
+ // machinery is skipped and `on` only pays one boolean test.
99
+ var DEV = typeof process !== 'undefined' && process.env && process.env.NODE_ENV !== 'production';
100
+ var DEFAULT_MAX_LISTENERS = 10;
101
+ var maxListenersMap = new WeakMap();
102
+ var warnedKeysMap = new WeakMap();
103
+ function _setMaxListeners(ee, n) {
104
+ maxListenersMap.set(ee, n);
105
+ }
106
+ function checkMaxListeners(ee, key, size) {
107
+ var max = maxListenersMap.get(ee);
108
+ if (max === undefined) max = DEFAULT_MAX_LISTENERS;
109
+ if (max <= 0 || size <= max) return;
110
+ var warnedKeys = warnedKeysMap.get(ee);
111
+ if (!warnedKeys) {
112
+ warnedKeys = new Set();
113
+ warnedKeysMap.set(ee, warnedKeys);
114
+ }
115
+ if (warnedKeys.has(key)) return;
116
+ warnedKeys.add(key);
117
+ // eslint-disable-next-line no-console
118
+ console.warn("Possible event-emitter memory leak: ".concat(size, " \"").concat(String(key), "\" listeners added. ") + "Use setMaxListeners(ee, n) to raise the limit, or 0 to disable this warning.");
119
+ }
120
+ var errorEvent = Symbol('error');
33
121
  function create$2() {
34
122
  return new Map();
35
123
  }
124
+
125
+ // A throwing handler must not stop its peers: errors are collected while the
126
+ // remaining handlers still run, then reported through emitError (the
127
+ // errorEvent channel). If no error handler is subscribed either, the first
128
+ // collected error is rethrown to the emit caller instead of being swallowed.
129
+ // Iteration follows Set semantics: a listener added while this emit runs is
130
+ // still visited by that same emit, and a listener removed before it is
131
+ // visited is skipped. Removing a whole key (off/removeAllListeners) does not
132
+ // affect an emit already in flight.
36
133
  function emit(ee, key) {
37
134
  for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
38
135
  args[_key - 2] = arguments[_key];
39
136
  }
40
- (ee.get(key) || []).forEach(function (h) {
41
- return h.apply(void 0, args);
137
+ var set = ee.get(key);
138
+ if (!set || !set.size) return;
139
+ var errors;
140
+ set.forEach(function (h) {
141
+ try {
142
+ h.apply(void 0, args);
143
+ } catch (err) {
144
+ if (errors) errors.push(err);else errors = [err];
145
+ }
146
+ });
147
+ if (errors) {
148
+ var _iterator = _createForOfIteratorHelper(errors),
149
+ _step;
150
+ try {
151
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
152
+ var err = _step.value;
153
+ emitError(ee, err);
154
+ }
155
+ } catch (err) {
156
+ _iterator.e(err);
157
+ } finally {
158
+ _iterator.f();
159
+ }
160
+ }
161
+ }
162
+ function emitError(ee, err) {
163
+ var handlerSet = ee.get(errorEvent);
164
+ if (!handlerSet || !handlerSet.size) throw err;
165
+ handlerSet.forEach(function (h) {
166
+ return h(err);
42
167
  });
43
168
  }
44
169
  function on(ee, key, handler) {
45
170
  var set = getSet(ee, key);
46
171
  set.add(handler);
172
+ if (DEV) checkMaxListeners(ee, key, set.size);
47
173
  return function () {
48
174
  return set.delete(handler);
49
175
  };
@@ -230,110 +356,59 @@
230
356
  return { value, key: JSON.stringify(value) };
231
357
  }
232
358
 
233
- const VALIDATION_OUTCOME = /* @__PURE__ */ Symbol("validation-outcome");
234
- function create(options) {
235
- const emitter = create$2();
236
- return {
237
- emitter,
238
- ...options,
239
- mode: options?.mode ?? "onSubmit",
240
- reValidateMode: options?.reValidateMode ?? "onChange",
241
- disabled: options?.disabled ?? false,
242
- validateDeps: options?.validateDeps ? new Set(options.validateDeps.map((dep) => create$1(dep).key)) : void 0,
243
- initialValues: options?.initialValues ?? {},
244
- values: /* @__PURE__ */ new Map(),
245
- deleted: /* @__PURE__ */ new Set(),
246
- errors: /* @__PURE__ */ new Map(),
247
- touched: /* @__PURE__ */ new Set(),
248
- validators: /* @__PURE__ */ new Map(),
249
- changeHandlers: /* @__PURE__ */ new Map(),
250
- validating: /* @__PURE__ */ new Set(),
251
- parsedValues: void 0,
252
- isSubmitting: false,
253
- submitCount: 0,
254
- isSubmitSuccessful: void 0
255
- };
256
- }
257
359
  const valuesCaches = /* @__PURE__ */ new WeakMap();
258
360
  function bumpValuesVersion(form) {
259
361
  const cache = valuesCaches.get(form);
260
362
  if (cache) cache.version++;
261
363
  }
262
- function getValues(form) {
263
- let cache = valuesCaches.get(form);
264
- if (!cache) {
265
- cache = { version: 0, result: computeValues(form) };
266
- valuesCaches.set(form, cache);
267
- } else if (cache.version > 0) {
268
- cache.result = computeValues(form);
269
- cache.version = 0;
270
- }
271
- return cache.result;
364
+ const dirtyBaselines = /* @__PURE__ */ new WeakMap();
365
+ function getDirtyBaseline(form, key, segments) {
366
+ const baselines = dirtyBaselines.get(form);
367
+ if (baselines?.has(key)) return baselines.get(key);
368
+ return get(form.initialValues, segments);
272
369
  }
273
- function computeValues(form) {
274
- const { initialValues, parsedValues, values, deleted } = form;
275
- const owned = /* @__PURE__ */ new Set();
276
- let merged = parsedValues ?? initialValues;
277
- for (const [key, value] of values) {
278
- merged = setOwned(merged, JSON.parse(key), value, owned);
279
- }
280
- for (const key of deleted) {
281
- merged = unset(merged, JSON.parse(key));
370
+ function setDirtyBaseline(form, { key }, value) {
371
+ let baselines = dirtyBaselines.get(form);
372
+ if (!baselines) {
373
+ baselines = /* @__PURE__ */ new Map();
374
+ dirtyBaselines.set(form, baselines);
282
375
  }
283
- return merged;
284
- }
285
- function getValue(form, name) {
286
- return getValueByPath(form, create$1(name));
376
+ baselines.set(key, value);
287
377
  }
288
- function getValueByPath({ initialValues, parsedValues, values, deleted }, path) {
289
- const { key, value: segments } = path;
290
- if (values.has(key)) return values.get(key);
291
- if (deleted.has(key)) return void 0;
292
- for (let i = segments.length - 1; i > 0; i--) {
293
- const ancestorKey = JSON.stringify(segments.slice(0, i));
294
- if (values.has(ancestorKey)) {
295
- return get(values.get(ancestorKey), segments.slice(i));
296
- }
378
+ function pruneDirtyBaselines(form, { key }) {
379
+ const baselines = dirtyBaselines.get(form);
380
+ if (!baselines?.size) return;
381
+ const stem = `${key.slice(0, -1)},`;
382
+ for (const baselineKey of baselines.keys()) {
383
+ if (baselineKey.startsWith(stem)) baselines.delete(baselineKey);
297
384
  }
298
- return get(parsedValues ?? initialValues, segments);
299
385
  }
300
- function setValue(form, name, value, options) {
301
- setValueByPath(form, create$1(name), value, options);
302
- }
303
- function setValueByPath(form, path, value, options) {
304
- const { emitter, values, deleted } = form;
305
- values.set(path.key, value);
306
- pruneDescendantKeys(values, path);
307
- reviveBranch(deleted, path);
308
- pruneDirtyBaselines(form, path);
309
- if (options?.shouldDirty === false) setDirtyBaseline(form, path, value);
310
- bumpDirtyVersion(form);
311
- bumpValuesVersion(form);
312
- if (options?.shouldTouch) setTouchedByPath(form, path);
313
- if (options?.shouldValidate) form.validators.get(path.key)?.();
314
- emit(emitter, "change", path);
386
+ function clearDirtyBaselines(form, key) {
387
+ const baselines = dirtyBaselines.get(form);
388
+ if (!baselines) return;
389
+ if (key === void 0) baselines.clear();
390
+ else baselines.delete(key);
315
391
  }
316
- function seedValueByPath(form, path, value) {
317
- const { values, deleted } = form;
318
- values.set(path.key, value);
319
- pruneDescendantKeys(values, path);
320
- reviveBranch(deleted, path);
321
- pruneDirtyBaselines(form, path);
322
- bumpDirtyVersion(form);
323
- bumpValuesVersion(form);
392
+ const dirtyFieldsCaches = /* @__PURE__ */ new WeakMap();
393
+ function bumpDirtyVersion(form) {
394
+ const cache = dirtyFieldsCaches.get(form);
395
+ if (cache) cache.version++;
324
396
  }
325
- function emitChangeByPath({ emitter }, path) {
326
- emit(emitter, "change", path);
397
+ function isSegmentsPath(name) {
398
+ return name.some((part) => typeof part === "number");
327
399
  }
328
- function changeValue(form, name, value, options) {
329
- changeValueByPath(form, create$1(name), value, options);
400
+ function isFieldError(value) {
401
+ return !!value && typeof value === "object" && typeof value.type === "string" && typeof value.message === "string";
330
402
  }
331
- function changeValueByPath(form, path, value, options) {
332
- if (options?.shouldDirty === false) setDirtyBaseline(form, path, value);
333
- const change = form.changeHandlers.get(path.key);
334
- if (change) change(value);
335
- else setValueByPath(form, path, value, options);
403
+ function setParsedValues(form, values) {
404
+ if (values === void 0 || values === form.parsedValues) return;
405
+ form.parsedValues = values;
406
+ bumpValuesVersion(form);
407
+ emit(form.emitter, "change");
336
408
  }
409
+
410
+ const FORM_ERROR = "_form";
411
+ const VALIDATION_OUTCOME = /* @__PURE__ */ Symbol("validation-outcome");
337
412
  function getError(form, name) {
338
413
  return getErrorByPath(form, create$1(name));
339
414
  }
@@ -358,29 +433,6 @@
358
433
  function getFirstError({ errors }) {
359
434
  return errors.values().next().value?.[0]?.message;
360
435
  }
361
- function getFieldState(form, name) {
362
- const path = create$1(name);
363
- const { values, touched, validating } = form;
364
- const live = values.get(path.key);
365
- return {
366
- value: getValueByPath(form, path),
367
- error: getErrorByPath(form, path),
368
- errors: getFieldErrorsByPath(form, path),
369
- // Same rule as getDirtyFields, committed baselines included: the field
370
- // is dirty while its live value differs from its effective baseline.
371
- isDirty: values.has(path.key) && getDirtyBaseline(form, path.key, path.value) !== live,
372
- isTouched: touched.has(path.key),
373
- isValidating: validating.has(path.key)
374
- };
375
- }
376
- function unsetValidatingByPath({ emitter, validating }, path) {
377
- validating.delete(path.key);
378
- emit(emitter, "validating", path);
379
- }
380
- function setValidatingByPath({ emitter, validating }, path) {
381
- validating.add(path.key);
382
- emit(emitter, "validating", path);
383
- }
384
436
  function setError(form, name, error) {
385
437
  setErrorByPath(form, create$1(name), error);
386
438
  }
@@ -430,6 +482,10 @@
430
482
  );
431
483
  }
432
484
  }
485
+ function hasErrors({ errors }) {
486
+ return errors.size > 0;
487
+ }
488
+
433
489
  function setTouched(form, name) {
434
490
  setTouchedByPath(form, create$1(name));
435
491
  }
@@ -444,97 +500,111 @@
444
500
  function hasTouchedByPath({ touched }, path) {
445
501
  return touched.has(path.key);
446
502
  }
447
- function isDirty(form) {
448
- let dirty = false;
449
- forEachDirtyField(form, () => {
450
- dirty = true;
451
- });
452
- return dirty;
453
- }
454
- function forEachDirtyField(form, fn) {
455
- for (const [key, value] of form.values) {
456
- const path = JSON.parse(key);
457
- if (getDirtyBaseline(form, key, path) !== value) fn(path.join("."));
458
- }
503
+ function getTouchedFields({ touched }) {
504
+ return Array.from(
505
+ touched,
506
+ (key) => JSON.parse(key).join(".")
507
+ );
459
508
  }
460
- const dirtyBaselines = /* @__PURE__ */ new WeakMap();
461
- function getDirtyBaseline(form, key, segments) {
462
- const baselines = dirtyBaselines.get(form);
463
- if (baselines?.has(key)) return baselines.get(key);
464
- return get(form.initialValues, segments);
509
+ function isTouched({ touched }) {
510
+ return touched.size > 0;
465
511
  }
466
- function setDirtyBaseline(form, { key }, value) {
467
- let baselines = dirtyBaselines.get(form);
468
- if (!baselines) {
469
- baselines = /* @__PURE__ */ new Map();
470
- dirtyBaselines.set(form, baselines);
512
+
513
+ function getValues(form) {
514
+ let cache = valuesCaches.get(form);
515
+ if (!cache) {
516
+ cache = { version: 0, result: computeValues(form) };
517
+ valuesCaches.set(form, cache);
518
+ } else if (cache.version > 0) {
519
+ cache.result = computeValues(form);
520
+ cache.version = 0;
471
521
  }
472
- baselines.set(key, value);
522
+ return cache.result;
473
523
  }
474
- function pruneDirtyBaselines(form, { key }) {
475
- const baselines = dirtyBaselines.get(form);
476
- if (!baselines?.size) return;
477
- const stem = `${key.slice(0, -1)},`;
478
- for (const baselineKey of baselines.keys()) {
479
- if (baselineKey.startsWith(stem)) baselines.delete(baselineKey);
524
+ function computeValues(form) {
525
+ const { initialValues, parsedValues, values, deleted } = form;
526
+ const owned = /* @__PURE__ */ new Set();
527
+ let merged = parsedValues ?? initialValues;
528
+ for (const [key, value] of values) {
529
+ merged = setOwned(merged, JSON.parse(key), value, owned);
480
530
  }
531
+ for (const key of deleted) {
532
+ merged = unset(merged, JSON.parse(key));
533
+ }
534
+ return merged;
481
535
  }
482
- function clearDirtyBaselines(form, key) {
483
- const baselines = dirtyBaselines.get(form);
484
- if (!baselines) return;
485
- if (key === void 0) baselines.clear();
486
- else baselines.delete(key);
536
+ function getValue(form, name) {
537
+ return getValueByPath(form, create$1(name));
487
538
  }
488
- const dirtyFieldsCaches = /* @__PURE__ */ new WeakMap();
489
- function bumpDirtyVersion(form) {
490
- const cache = dirtyFieldsCaches.get(form);
491
- if (cache) cache.version++;
539
+ function getValueByPath({ initialValues, parsedValues, values, deleted }, path) {
540
+ const { key, value: segments } = path;
541
+ if (values.has(key)) return values.get(key);
542
+ if (deleted.has(key)) return void 0;
543
+ for (let i = segments.length - 1; i > 0; i--) {
544
+ const ancestorKey = JSON.stringify(segments.slice(0, i));
545
+ if (values.has(ancestorKey)) {
546
+ return get(values.get(ancestorKey), segments.slice(i));
547
+ }
548
+ }
549
+ return get(parsedValues ?? initialValues, segments);
492
550
  }
493
- function computeDirtyFields(form) {
494
- const dirtyFields = {};
495
- forEachDirtyField(form, (key) => {
496
- dirtyFields[key] = true;
497
- });
498
- return dirtyFields;
551
+ function setValue(form, name, value, options) {
552
+ setValueByPath(form, create$1(name), value, options);
499
553
  }
500
- function sameDirtyKeys(a, b) {
501
- const aKeys = Object.keys(a);
502
- if (aKeys.length !== Object.keys(b).length) return false;
503
- return aKeys.every((key) => b[key] === true);
554
+ function setValueByPath(form, path, value, options) {
555
+ const { emitter, values, deleted } = form;
556
+ values.set(path.key, value);
557
+ pruneDescendantKeys(values, path);
558
+ reviveBranch(deleted, path);
559
+ pruneDirtyBaselines(form, path);
560
+ if (options?.shouldDirty === false) setDirtyBaseline(form, path, value);
561
+ bumpDirtyVersion(form);
562
+ bumpValuesVersion(form);
563
+ if (options?.shouldTouch) setTouchedByPath(form, path);
564
+ if (options?.shouldValidate) form.validators.get(path.key)?.();
565
+ emit(emitter, "change", path);
504
566
  }
505
- function getDirtyFields(form) {
506
- let cache = dirtyFieldsCaches.get(form);
507
- if (!cache) {
508
- cache = { version: 0, result: computeDirtyFields(form) };
509
- dirtyFieldsCaches.set(form, cache);
510
- } else if (cache.version > 0) {
511
- const result = computeDirtyFields(form);
512
- if (!sameDirtyKeys(cache.result, result)) cache.result = result;
513
- cache.version = 0;
514
- }
515
- return cache.result;
567
+ function seedValueByPath(form, path, value) {
568
+ const { values, deleted } = form;
569
+ values.set(path.key, value);
570
+ pruneDescendantKeys(values, path);
571
+ reviveBranch(deleted, path);
572
+ pruneDirtyBaselines(form, path);
573
+ bumpDirtyVersion(form);
574
+ bumpValuesVersion(form);
516
575
  }
517
- function getTouchedFields({ touched }) {
518
- return Array.from(
519
- touched,
520
- (key) => JSON.parse(key).join(".")
521
- );
576
+ function emitChangeByPath({ emitter }, path) {
577
+ emit(emitter, "change", path);
522
578
  }
523
- function isTouched({ touched }) {
524
- return touched.size > 0;
579
+ function getFieldState(form, name) {
580
+ const path = create$1(name);
581
+ const { values, touched, validating } = form;
582
+ const live = values.get(path.key);
583
+ return {
584
+ value: getValueByPath(form, path),
585
+ error: getErrorByPath(form, path),
586
+ errors: getFieldErrorsByPath(form, path),
587
+ // Same rule as getDirtyFields, committed baselines included: the field
588
+ // is dirty while its live value differs from its effective baseline.
589
+ isDirty: values.has(path.key) && getDirtyBaseline(form, path.key, path.value) !== live,
590
+ isTouched: touched.has(path.key),
591
+ isValidating: validating.has(path.key)
592
+ };
525
593
  }
526
- function removeField(form, name) {
527
- removeFieldByPath(form, create$1(name));
594
+ function removeField(form, name, options) {
595
+ removeFieldByPath(form, create$1(name), options);
528
596
  }
529
- function removeFieldByPath(form, path) {
597
+ function removeFieldByPath(form, path, options) {
530
598
  const { key, value: segments } = path;
531
599
  const { emitter, values, touched, errors, validating, deleted } = form;
532
- values.delete(key);
533
- touched.delete(key);
534
- errors.delete(key);
600
+ if (!options?.keepValue && !options?.keepDirty) {
601
+ values.delete(key);
602
+ clearDirtyBaselines(form, key);
603
+ if (!hasLiveBranch(values, segments)) deleted.add(key);
604
+ }
605
+ if (!options?.keepTouched) touched.delete(key);
606
+ if (!options?.keepError) errors.delete(key);
535
607
  validating.delete(key);
536
- clearDirtyBaselines(form, key);
537
- if (!hasLiveBranch(values, segments)) deleted.add(key);
538
608
  bumpDirtyVersion(form);
539
609
  bumpValuesVersion(form);
540
610
  emit(emitter, "change", path);
@@ -580,17 +650,37 @@
580
650
  bumpValuesVersion(form);
581
651
  emit(form.emitter, "change");
582
652
  }
653
+ function collectValueLeaves(node, segments, out) {
654
+ if (node !== null && typeof node === "object") {
655
+ if (Array.isArray(node)) {
656
+ for (let i = 0; i < node.length; i++) {
657
+ collectValueLeaves(node[i], [...segments, i], out);
658
+ }
659
+ return;
660
+ }
661
+ const keys = Object.keys(node);
662
+ if (keys.length > 0) {
663
+ for (const k of keys) {
664
+ collectValueLeaves(node[k], [...segments, k], out);
665
+ }
666
+ return;
667
+ }
668
+ }
669
+ out.push({ segments, value: node });
670
+ }
583
671
  function reset(form, initialValues, options) {
584
- const dirtyValues = [];
585
- if (options?.keepDirtyValues) {
672
+ const keptValues = [];
673
+ if (options?.keepValues) {
674
+ collectValueLeaves(getValues(form), [], keptValues);
675
+ } else if (options?.keepDirtyValues) {
586
676
  for (const [key, value] of form.values) {
587
677
  const segments = JSON.parse(key);
588
678
  if (getDirtyBaseline(form, key, segments) !== value) {
589
- dirtyValues.push({ segments, value });
679
+ keptValues.push({ segments, value });
590
680
  }
591
681
  }
592
682
  }
593
- form.initialValues = initialValues;
683
+ form.initialValues = options?.keepDefaultValues ? form.initialValues : initialValues ?? form.initialValues;
594
684
  form.parsedValues = void 0;
595
685
  if (!options?.keepErrors) clearErrors(form);
596
686
  const { emitter, touched, values, deleted, validating } = form;
@@ -604,7 +694,7 @@
604
694
  if (!options?.keepIsSubmitted) form.isSubmitSuccessful = void 0;
605
695
  bumpDirtyVersion(form);
606
696
  bumpValuesVersion(form);
607
- for (const { segments, value } of dirtyValues) {
697
+ for (const { segments, value } of keptValues) {
608
698
  setValueByPath(form, create$1(segments), value);
609
699
  }
610
700
  emit(emitter, "change");
@@ -639,10 +729,172 @@
639
729
  bumpDirtyVersion(form);
640
730
  bumpValuesVersion(form);
641
731
  }
642
- function hasErrors({ errors }) {
643
- return errors.size > 0;
732
+
733
+ function isDirty(form) {
734
+ let dirty = false;
735
+ forEachDirtyField(form, () => {
736
+ dirty = true;
737
+ });
738
+ return dirty;
739
+ }
740
+ function forEachDirtyField(form, fn) {
741
+ for (const [key, value] of form.values) {
742
+ const path = JSON.parse(key);
743
+ if (getDirtyBaseline(form, key, path) !== value) fn(path.join("."));
744
+ }
745
+ }
746
+ function computeDirtyFields(form) {
747
+ const dirtyFields = {};
748
+ forEachDirtyField(form, (key) => {
749
+ dirtyFields[key] = true;
750
+ });
751
+ return dirtyFields;
752
+ }
753
+ function sameDirtyKeys(a, b) {
754
+ const aKeys = Object.keys(a);
755
+ if (aKeys.length !== Object.keys(b).length) return false;
756
+ return aKeys.every((key) => b[key] === true);
757
+ }
758
+ function getDirtyFields(form) {
759
+ let cache = dirtyFieldsCaches.get(form);
760
+ if (!cache) {
761
+ cache = { version: 0, result: computeDirtyFields(form) };
762
+ dirtyFieldsCaches.set(form, cache);
763
+ } else if (cache.version > 0) {
764
+ const result = computeDirtyFields(form);
765
+ if (!sameDirtyKeys(cache.result, result)) cache.result = result;
766
+ cache.version = 0;
767
+ }
768
+ return cache.result;
769
+ }
770
+
771
+ function unsetValidatingByPath({ emitter, validating }, path) {
772
+ validating.delete(path.key);
773
+ emit(emitter, "validating", path);
774
+ }
775
+ function setValidatingByPath({ emitter, validating }, path) {
776
+ validating.add(path.key);
777
+ emit(emitter, "validating", path);
778
+ }
779
+ function registerValidatorByPath(form, path, registration) {
780
+ let timer = null;
781
+ let controller = null;
782
+ let marked = false;
783
+ let lock = null;
784
+ let errorSource = null;
785
+ const hasErrors2 = (errors) => errors !== void 0 && !(Array.isArray(errors) && errors.length === 0);
786
+ const mark = () => {
787
+ if (marked) return;
788
+ marked = true;
789
+ setValidatingByPath(form, path);
790
+ };
791
+ const unmark = () => {
792
+ if (!marked) return;
793
+ marked = false;
794
+ unsetValidatingByPath(form, path);
795
+ };
796
+ const runSync = () => {
797
+ const sync = registration.sync();
798
+ if (!sync) return false;
799
+ const errors = sync(getValueByPath(form, path), { form, path });
800
+ if (!hasErrors2(errors)) {
801
+ if (!registration.validate() || errorSource === "sync") {
802
+ setErrorByPath(form, path, void 0);
803
+ errorSource = null;
804
+ }
805
+ return false;
806
+ }
807
+ setErrorByPath(form, path, errors);
808
+ errorSource = "sync";
809
+ return true;
810
+ };
811
+ const supersede = () => {
812
+ if (timer !== null) {
813
+ clearTimeout(timer);
814
+ timer = null;
815
+ }
816
+ controller?.abort();
817
+ lock = {};
818
+ };
819
+ const runValidator = () => {
820
+ const fn = registration.validate();
821
+ if (!fn) {
822
+ unmark();
823
+ return;
824
+ }
825
+ controller?.abort();
826
+ controller = new AbortController();
827
+ const round = lock = {};
828
+ let result;
829
+ try {
830
+ result = fn(getValueByPath(form, path), {
831
+ form,
832
+ path,
833
+ signal: controller.signal
834
+ });
835
+ } catch (e) {
836
+ unmark();
837
+ throw e;
838
+ }
839
+ if (!isPromise(result)) {
840
+ setErrorByPath(form, path, result);
841
+ errorSource = hasErrors2(result) ? "validator" : null;
842
+ unmark();
843
+ return;
844
+ }
845
+ mark();
846
+ result.then(
847
+ (error) => {
848
+ if (lock === round) {
849
+ setErrorByPath(form, path, error);
850
+ errorSource = hasErrors2(error) ? "validator" : null;
851
+ }
852
+ }
853
+ ).catch(() => {
854
+ }).finally(() => {
855
+ if (lock === round) {
856
+ unmark();
857
+ lock = null;
858
+ }
859
+ });
860
+ };
861
+ const run = () => {
862
+ timer = null;
863
+ if (runSync()) {
864
+ supersede();
865
+ unmark();
866
+ return;
867
+ }
868
+ runValidator();
869
+ };
870
+ const kick = () => {
871
+ if (runSync()) {
872
+ supersede();
873
+ unmark();
874
+ return;
875
+ }
876
+ if (!registration.validate()) return;
877
+ const debounce = registration.debounce();
878
+ if (debounce > 0) {
879
+ if (timer !== null) clearTimeout(timer);
880
+ else mark();
881
+ timer = setTimeout(run, debounce);
882
+ return;
883
+ }
884
+ runValidator();
885
+ };
886
+ form.validators.set(path.key, kick);
887
+ return () => {
888
+ form.validators.delete(path.key);
889
+ if (timer !== null) {
890
+ clearTimeout(timer);
891
+ timer = null;
892
+ }
893
+ unmark();
894
+ controller?.abort();
895
+ };
644
896
  }
645
- async function trigger(form, name) {
897
+ async function trigger(form, name, options) {
646
898
  const settle = (keys2) => waitUntil(
647
899
  form.emitter,
648
900
  "validating",
@@ -653,18 +905,25 @@
653
905
  form.validators.forEach((validator) => validator());
654
906
  await settle();
655
907
  if (form.validate) await runFormValidate(form);
908
+ if (options?.shouldTouch) touchKeys(form, [...form.validators.keys()]);
909
+ if (options?.shouldFocus) {
910
+ const firstKey = form.errors.keys().next().value;
911
+ if (firstKey !== void 0) emit(form.emitter, "focusError", firstKey);
912
+ }
656
913
  return !hasErrors(form);
657
914
  }
658
915
  const keys = typeof name === "string" || isSegmentsPath(name) ? [create$1(name).key] : name.map((one) => create$1(one).key);
659
916
  keys.forEach((key) => form.validators.get(key)?.());
660
917
  await settle(keys);
918
+ if (options?.shouldTouch) touchKeys(form, keys);
919
+ if (options?.shouldFocus) {
920
+ const firstKey = keys.find((key) => form.errors.has(key));
921
+ if (firstKey !== void 0) emit(form.emitter, "focusError", firstKey);
922
+ }
661
923
  return keys.every((key) => !form.errors.has(key));
662
924
  }
663
- function isSegmentsPath(name) {
664
- return name.some((part) => typeof part === "number");
665
- }
666
- function isFieldError(value) {
667
- return !!value && typeof value === "object" && typeof value.type === "string" && typeof value.message === "string";
925
+ function touchKeys(form, keys) {
926
+ keys.forEach((key) => setTouchedByPath(form, create$1(JSON.parse(key))));
668
927
  }
669
928
  function setFormErrors(form, result, segments = [], footprint) {
670
929
  Object.entries(result).forEach(([key, value]) => {
@@ -694,12 +953,6 @@
694
953
  const stored = form.errors.get(path.key);
695
954
  if (stored) footprint.set(path.key, stored);
696
955
  }
697
- function setParsedValues(form, values) {
698
- if (values === void 0 || values === form.parsedValues) return;
699
- form.parsedValues = values;
700
- bumpValuesVersion(form);
701
- emit(form.emitter, "change");
702
- }
703
956
  function applyValidateResult(form, result) {
704
957
  const footprint = form.validateDeps ? getFormErrorFootprint(form) : void 0;
705
958
  if (footprint) {
@@ -897,6 +1150,55 @@
897
1150
  async function validate(form) {
898
1151
  return ensureValidate(form).catch((e) => e.message);
899
1152
  }
1153
+
1154
+ const fieldModes = /* @__PURE__ */ new WeakMap();
1155
+ function changeValue(form, name, value, options) {
1156
+ changeValueByPath(form, create$1(name), value, options);
1157
+ }
1158
+ function changeValueByPath(form, path, value, options) {
1159
+ if (options?.shouldDirty === false) setDirtyBaseline(form, path, value);
1160
+ if (fieldModes.get(form)?.get(path.key)) {
1161
+ userChangeByPath(form, path, value);
1162
+ } else {
1163
+ setValueByPath(form, path, value, options);
1164
+ }
1165
+ }
1166
+ function registerFieldMode(form, path, mode) {
1167
+ let modes = fieldModes.get(form);
1168
+ if (!modes) {
1169
+ modes = /* @__PURE__ */ new Map();
1170
+ fieldModes.set(form, modes);
1171
+ }
1172
+ const displaced = modes.has(path.key);
1173
+ const token = {};
1174
+ modes.set(path.key, { mode, token });
1175
+ return { token, displaced };
1176
+ }
1177
+ function unregisterFieldMode(form, path, token) {
1178
+ const modes = fieldModes.get(form);
1179
+ const entry = modes?.get(path.key);
1180
+ if (modes && entry && entry.token === token) modes.delete(path.key);
1181
+ }
1182
+ function runUserChangeGate(form, path, mode) {
1183
+ if (mode === "onChange" || mode === "all" || mode === "onTouched" && hasTouchedByPath(form, path) || getFieldErrorsByPath(form, path).length > 0 && form.reValidateMode === "onChange")
1184
+ form.validators.get(path.key)?.();
1185
+ revalidateFormOnChange(form, path, mode);
1186
+ revalidateDependentsOnChange(form, path, mode);
1187
+ }
1188
+ function userChangeByPath(form, path, value) {
1189
+ setValueByPath(form, path, value);
1190
+ const entry = fieldModes.get(form)?.get(path.key);
1191
+ if (entry) runUserChangeGate(form, path, entry.mode ?? form.mode);
1192
+ }
1193
+ function userBlur(form, path) {
1194
+ setTouchedByPath(form, path);
1195
+ const entry = fieldModes.get(form)?.get(path.key);
1196
+ if (!entry) return;
1197
+ const mode = entry.mode ?? form.mode;
1198
+ if (mode === "onBlur" || mode === "onTouched" || mode === "all" || getFieldErrorsByPath(form, path).length > 0 && form.reValidateMode === "onBlur")
1199
+ form.validators.get(path.key)?.();
1200
+ }
1201
+
900
1202
  function setIsSubmitting(form, value) {
901
1203
  form.isSubmitting = value;
902
1204
  emit(form.emitter, "submitting");
@@ -943,6 +1245,7 @@
943
1245
  onSubmit,
944
1246
  onValidSubmit,
945
1247
  onInvalidSubmit,
1248
+ onAction,
946
1249
  shouldFocusError = true
947
1250
  } = options ?? {};
948
1251
  return async (e) => {
@@ -979,6 +1282,7 @@
979
1282
  const submitted = getValues(form);
980
1283
  if (onSubmit) await onSubmit(submitted, e);
981
1284
  if (onValidSubmit) await onValidSubmit(submitted, e);
1285
+ if (onAction) await onAction(submitted, e);
982
1286
  setSubmitSuccessful(form, true);
983
1287
  } catch {
984
1288
  setSubmitSuccessful(form, false);
@@ -987,12 +1291,58 @@
987
1291
  }
988
1292
  };
989
1293
  }
1294
+
990
1295
  function setFocus(form, name, options) {
991
1296
  const { key } = create$1(name);
992
1297
  if (options) emit(form.emitter, "focusError", key, options);
993
1298
  else emit(form.emitter, "focusError", key);
994
1299
  }
995
1300
 
1301
+ function create(options) {
1302
+ const emitter = create$2();
1303
+ _setMaxListeners(emitter, 0);
1304
+ let source = options?.initialValues ?? {};
1305
+ if (typeof source === "function") source = source();
1306
+ const form = {
1307
+ emitter,
1308
+ ...options,
1309
+ mode: options?.mode ?? "onSubmit",
1310
+ reValidateMode: options?.reValidateMode ?? "onChange",
1311
+ disabled: options?.disabled ?? false,
1312
+ validateDeps: options?.validateDeps ? new Set(options.validateDeps.map((dep) => create$1(dep).key)) : void 0,
1313
+ initialValues: {},
1314
+ values: /* @__PURE__ */ new Map(),
1315
+ deleted: /* @__PURE__ */ new Set(),
1316
+ errors: /* @__PURE__ */ new Map(),
1317
+ touched: /* @__PURE__ */ new Set(),
1318
+ validators: /* @__PURE__ */ new Map(),
1319
+ validating: /* @__PURE__ */ new Set(),
1320
+ parsedValues: void 0,
1321
+ isSubmitting: false,
1322
+ submitCount: 0,
1323
+ isSubmitSuccessful: void 0,
1324
+ isLoading: false
1325
+ };
1326
+ if (isPromise(source)) {
1327
+ form.isLoading = true;
1328
+ emit(emitter, "loading");
1329
+ Promise.resolve(source).then(
1330
+ (resolved) => {
1331
+ form.isLoading = false;
1332
+ emit(emitter, "loading");
1333
+ setInitialValues(form, resolved ?? {});
1334
+ },
1335
+ (error) => {
1336
+ form.isLoading = false;
1337
+ emit(emitter, "loading");
1338
+ }
1339
+ );
1340
+ } else {
1341
+ form.initialValues = source;
1342
+ }
1343
+ return form;
1344
+ }
1345
+
996
1346
  function defaultMessage(type, bound) {
997
1347
  switch (type) {
998
1348
  case "required":
@@ -1302,6 +1652,9 @@
1302
1652
  seededRef.current = { done: false, source: void 0 };
1303
1653
  React.useEffect(() => {
1304
1654
  if (initialValues === void 0) return;
1655
+ if (typeof initialValues === "function" || isPromise(initialValues)) {
1656
+ return;
1657
+ }
1305
1658
  const seeded = seededRef.current;
1306
1659
  if (seeded.done && (seeded.source === initialValues || isEqual(seeded.source, initialValues))) {
1307
1660
  return;
@@ -1326,12 +1679,14 @@
1326
1679
  }, [form, values]);
1327
1680
  return form;
1328
1681
  }
1329
- function useWatchCore(subscribeFactory, getter) {
1682
+ function useWatchCore(subscribeFactory, getter, isEqual2) {
1330
1683
  const cacheRef = React.useRef(null);
1331
1684
  if (cacheRef.current === null) cacheRef.current = { hasValue: false };
1332
1685
  const cache = cacheRef.current;
1333
1686
  const getterRef = React.useRef(getter);
1334
1687
  getterRef.current = getter;
1688
+ const isEqualRef = React.useRef(isEqual2);
1689
+ isEqualRef.current = isEqual2;
1335
1690
  const getSnapshot = React.useCallback(() => {
1336
1691
  if (!cache.hasValue) {
1337
1692
  cache.value = getterRef.current();
@@ -1343,6 +1698,14 @@
1343
1698
  (notify) => {
1344
1699
  cache.hasValue = false;
1345
1700
  const invalidate = () => {
1701
+ const compare = isEqualRef.current;
1702
+ if (compare && cache.hasValue) {
1703
+ const next = getterRef.current();
1704
+ if (compare(cache.value, next)) return;
1705
+ cache.value = next;
1706
+ notify();
1707
+ return;
1708
+ }
1346
1709
  cache.hasValue = false;
1347
1710
  notify();
1348
1711
  };
@@ -1352,12 +1715,13 @@
1352
1715
  );
1353
1716
  return shimExports.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
1354
1717
  }
1355
- function useWatch(emitter, event, getter) {
1718
+ function useWatch(formOrEmitter, event, getter, isEqual2) {
1719
+ const emitter = "emitter" in formOrEmitter ? formOrEmitter.emitter : formOrEmitter;
1356
1720
  const subscribeFactory = React.useCallback(
1357
1721
  (invalidate) => on(emitter, event, invalidate),
1358
1722
  [emitter, event]
1359
1723
  );
1360
- return useWatchCore(subscribeFactory, getter);
1724
+ return useWatchCore(subscribeFactory, getter, isEqual2);
1361
1725
  }
1362
1726
  function useValue(form, name) {
1363
1727
  return useValueByPath(form, create$1(name));
@@ -1415,19 +1779,71 @@
1415
1779
  );
1416
1780
  }
1417
1781
  function useIsDirty(form) {
1418
- return useWatch(form.emitter, "change", isDirty.bind(null, form));
1782
+ return useWatch(form, "change", isDirty.bind(null, form));
1419
1783
  }
1420
1784
  function useDirtyFields(form) {
1421
- return useWatch(form.emitter, "change", getDirtyFields.bind(null, form));
1785
+ return useWatch(form, "change", getDirtyFields.bind(null, form));
1422
1786
  }
1423
1787
  function useTouchedFields(form) {
1424
- return useWatch(form.emitter, "touched", getTouchedFields.bind(null, form));
1788
+ return useWatch(form, "touched", getTouchedFields.bind(null, form));
1789
+ }
1790
+ const FORM_STATE_EVENTS = [
1791
+ "change",
1792
+ "errors",
1793
+ "touched",
1794
+ "validating",
1795
+ "submitting",
1796
+ "submitCount",
1797
+ "submitSuccessful",
1798
+ "disabled",
1799
+ "loading"
1800
+ ];
1801
+ function getFormState(form) {
1802
+ return {
1803
+ isDirty: isDirty(form),
1804
+ dirtyFields: getDirtyFields(form),
1805
+ isTouched: form.touched.size > 0,
1806
+ touchedFields: getTouchedFields(form),
1807
+ hasErrors: hasErrors(form),
1808
+ isValid: !hasErrors(form),
1809
+ isSubmitting: form.isSubmitting,
1810
+ isValidating: form.validating.size > 0,
1811
+ isSubmitSuccessful: form.isSubmitSuccessful,
1812
+ submitCount: form.submitCount,
1813
+ isLoading: form.isLoading,
1814
+ disabled: form.disabled
1815
+ };
1816
+ }
1817
+ function isSameFormState(a, b) {
1818
+ const sameTouched = a.touchedFields.length === b.touchedFields.length && a.touchedFields.every((path, i) => path === b.touchedFields[i]);
1819
+ return a.isDirty === b.isDirty && a.dirtyFields === b.dirtyFields && a.isTouched === b.isTouched && sameTouched && a.hasErrors === b.hasErrors && a.isValid === b.isValid && a.isSubmitting === b.isSubmitting && a.isValidating === b.isValidating && a.isSubmitSuccessful === b.isSubmitSuccessful && a.submitCount === b.submitCount && a.isLoading === b.isLoading && a.disabled === b.disabled;
1820
+ }
1821
+ function useFormState(form) {
1822
+ const getter = React.useCallback(() => getFormState(form), [form]);
1823
+ const subscribeFactory = React.useCallback(
1824
+ (invalidate) => {
1825
+ const offs = FORM_STATE_EVENTS.map(
1826
+ (event) => on(form.emitter, event, invalidate)
1827
+ );
1828
+ return () => {
1829
+ for (const off of offs) off();
1830
+ };
1831
+ },
1832
+ [form.emitter]
1833
+ );
1834
+ return useWatchCore(subscribeFactory, getter, isSameFormState);
1425
1835
  }
1426
1836
  function useHasErrors(form) {
1427
- return useWatch(form.emitter, "errors", hasErrors.bind(null, form));
1837
+ return useWatch(form, "errors", hasErrors.bind(null, form));
1838
+ }
1839
+ function useIsValid(form) {
1840
+ return useWatch(form, "errors", () => !hasErrors(form));
1428
1841
  }
1429
1842
  function useIsSubmitting(form) {
1430
- return useWatch(form.emitter, "submitting", () => form.isSubmitting);
1843
+ return useWatch(form, "submitting", () => form.isSubmitting);
1844
+ }
1845
+ function useIsLoading(form) {
1846
+ return useWatch(form, "loading", () => form.isLoading);
1431
1847
  }
1432
1848
  function useCanSubmit(form) {
1433
1849
  const { emitter } = form;
@@ -1448,7 +1864,23 @@
1448
1864
  );
1449
1865
  }
1450
1866
  function useSubmitCount(form) {
1451
- return useWatch(form.emitter, "submitCount", () => form.submitCount);
1867
+ return useWatch(form, "submitCount", () => form.submitCount);
1868
+ }
1869
+ function useIsValidating(form) {
1870
+ return useWatch(form, "validating", () => form.validating.size > 0);
1871
+ }
1872
+ function useIsSubmitSuccessful(form) {
1873
+ return useWatch(
1874
+ form.emitter,
1875
+ "submitSuccessful",
1876
+ () => !!form.isSubmitSuccessful
1877
+ );
1878
+ }
1879
+ function useFormError(form) {
1880
+ return useErrorByPath(form, create$1(FORM_ERROR))?.message;
1881
+ }
1882
+ function useFormErrors(form) {
1883
+ return useFieldErrorsByPath(form, create$1(FORM_ERROR));
1452
1884
  }
1453
1885
 
1454
1886
  function usePath(name) {
@@ -1473,92 +1905,29 @@
1473
1905
  const contextForm = React.useContext(FormContext);
1474
1906
  const form = formProp || contextForm;
1475
1907
  if (!form) throw new Error("no form provided");
1476
- const lockRef = React.useRef(null);
1477
1908
  const validateRef = React.useRef(validate);
1478
1909
  validateRef.current = validate;
1479
1910
  const debounceRef = React.useRef(options?.debounce ?? 0);
1480
1911
  debounceRef.current = options?.debounce ?? 0;
1481
- React.useEffect(() => {
1482
- let timer = null;
1483
- let controller = null;
1484
- let marked = false;
1485
- const mark = () => {
1486
- if (marked) return;
1487
- marked = true;
1488
- setValidatingByPath(form, path);
1489
- };
1490
- const unmark = () => {
1491
- if (!marked) return;
1492
- marked = false;
1493
- unsetValidatingByPath(form, path);
1494
- };
1495
- const run = () => {
1496
- timer = null;
1497
- const fn = validateRef.current;
1498
- if (!fn) {
1499
- unmark();
1500
- return;
1501
- }
1502
- controller?.abort();
1503
- controller = new AbortController();
1504
- const lock = lockRef.current = {};
1505
- let result;
1506
- try {
1507
- result = fn(getValueByPath(form, path), {
1508
- form,
1509
- path,
1510
- signal: controller.signal
1511
- });
1512
- } catch (e) {
1513
- unmark();
1514
- throw e;
1515
- }
1516
- if (!isPromise(result)) {
1517
- setErrorByPath(form, path, result);
1518
- unmark();
1519
- return;
1520
- }
1521
- mark();
1522
- result.then(
1523
- (error) => {
1524
- if (lock === lockRef.current) {
1525
- setErrorByPath(form, path, error);
1526
- }
1527
- }
1528
- ).catch(() => {
1529
- }).finally(() => {
1530
- if (lock === lockRef.current) {
1531
- unmark();
1532
- lockRef.current = null;
1533
- }
1534
- });
1535
- };
1536
- const validator = () => {
1537
- const debounce = debounceRef.current;
1538
- if (debounce > 0) {
1539
- if (timer !== null) clearTimeout(timer);
1540
- else mark();
1541
- timer = setTimeout(run, debounce);
1542
- return;
1543
- }
1544
- run();
1545
- };
1546
- form.validators.set(path.key, validator);
1547
- return () => {
1548
- form.validators.delete(path.key);
1549
- if (timer !== null) {
1550
- clearTimeout(timer);
1551
- timer = null;
1552
- }
1553
- unmark();
1554
- controller?.abort();
1555
- };
1556
- }, [form, path.key]);
1912
+ const syncRef = React.useRef(options?.sync);
1913
+ syncRef.current = options?.sync;
1914
+ React.useEffect(
1915
+ () => registerValidatorByPath(form, path, {
1916
+ validate: () => validateRef.current,
1917
+ debounce: () => debounceRef.current,
1918
+ sync: () => syncRef.current
1919
+ }),
1920
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- deps are `path.key` on purpose: usePath returns a stable Path per key, so re-subscribing on key (not object identity) is enough
1921
+ [form, path.key]
1922
+ );
1557
1923
  return useStageFn(() => form.validators.get(path.key)?.());
1558
1924
  }
1559
1925
 
1926
+ function hasRuleConstraints(rules) {
1927
+ return rules.required !== void 0 || rules.min !== void 0 || rules.max !== void 0 || rules.minLength !== void 0 || rules.maxLength !== void 0 || rules.pattern !== void 0;
1928
+ }
1560
1929
  function combineRulesAndValidate(rules, validate) {
1561
- if (!rules) return validate;
1930
+ if (!rules || !hasRuleConstraints(rules)) return validate;
1562
1931
  const ruleValidator = rulesToValidator(rules);
1563
1932
  if (!validate) return ruleValidator;
1564
1933
  return (value, meta) => {
@@ -1590,6 +1959,25 @@
1590
1959
  }, [errors, delay, shown]);
1591
1960
  return delay === void 0 ? errors : shown;
1592
1961
  }
1962
+ function useFieldValue(form, path, uncontrolled) {
1963
+ const snapshotRef = React.useRef({
1964
+ has: false,
1965
+ value: void 0
1966
+ });
1967
+ const getter = React.useCallback(() => {
1968
+ if (!uncontrolled) return getValueByPath(form, path);
1969
+ if (!snapshotRef.current.has) {
1970
+ snapshotRef.current = { has: true, value: getValueByPath(form, path) };
1971
+ }
1972
+ return snapshotRef.current.value;
1973
+ }, [uncontrolled, form, path]);
1974
+ const subscribeFactory = React.useCallback(
1975
+ (invalidate) => uncontrolled ? () => {
1976
+ } : onPathEvent(form.emitter, "change", path, "leaf", invalidate),
1977
+ [uncontrolled, form.emitter, path]
1978
+ );
1979
+ return useWatchCore(subscribeFactory, getter);
1980
+ }
1593
1981
  function useFieldCore({
1594
1982
  form: f1,
1595
1983
  name,
@@ -1601,6 +1989,7 @@
1601
1989
  validateDeps,
1602
1990
  delayError,
1603
1991
  disabled,
1992
+ uncontrolled,
1604
1993
  mode: modeOption
1605
1994
  }, Context) {
1606
1995
  const contextForm = React.useContext(Context);
@@ -1618,35 +2007,23 @@
1618
2007
  seededRef.current = false;
1619
2008
  emitChangeByPath(form, path);
1620
2009
  });
1621
- const validator = useValidate(
1622
- combineRulesAndValidate(rules, validate),
1623
- path,
1624
- form,
1625
- {
1626
- debounce: validateDebounce
1627
- }
1628
- );
2010
+ const restRules = rules ? { ...rules, required: void 0 } : void 0;
2011
+ useValidate(combineRulesAndValidate(restRules, validate), path, form, {
2012
+ debounce: validateDebounce,
2013
+ sync: rules && rules.required !== void 0 ? rulesToValidator({ required: rules.required }) : void 0
2014
+ });
1629
2015
  const liveErrors = useFieldErrorsByPath(form, path);
1630
2016
  const errors = useDelayedErrors(liveErrors, delayError);
1631
2017
  const errorObject = errors[0];
1632
2018
  const error = errorObject?.message;
1633
- const value = useValueByPath(form, path);
1634
- const formDisabled = useWatch(form.emitter, "disabled", () => form.disabled);
1635
- const mode = modeOption ?? form.mode;
1636
- const onChange = useStageFn((v) => {
1637
- setValueByPath(form, path, v);
1638
- if (mode === "onChange" || mode === "all" || mode === "onTouched" && hasTouchedByPath(form, path) || liveErrors.length > 0 && form.reValidateMode === "onChange")
1639
- validator();
1640
- revalidateFormOnChange(form, path, mode);
1641
- revalidateDependentsOnChange(form, path, mode);
1642
- });
2019
+ const value = useFieldValue(form, path, !!uncontrolled);
2020
+ const formDisabled = useWatch(form, "disabled", () => form.disabled);
2021
+ const onChange = useStageFn((v) => userChangeByPath(form, path, v));
2022
+ const onBlur = useStageFn(() => userBlur(form, path));
1643
2023
  React.useEffect(() => {
1644
- form.changeHandlers.set(path.key, onChange);
1645
- return () => {
1646
- if (form.changeHandlers.get(path.key) === onChange)
1647
- form.changeHandlers.delete(path.key);
1648
- };
1649
- }, [form, path.key, onChange]);
2024
+ const { token, displaced } = registerFieldMode(form, path, modeOption);
2025
+ return () => unregisterFieldMode(form, path, token);
2026
+ }, [form, path.key, modeOption]);
1650
2027
  const depsKey = validateDeps?.length ? validateDeps.join("\n") : void 0;
1651
2028
  React.useEffect(() => {
1652
2029
  if (!depsKey) return;
@@ -1654,14 +2031,29 @@
1654
2031
  registerFieldValidateDeps(form, path.key, depKeys);
1655
2032
  return () => unregisterFieldValidateDeps(form, path.key, depKeys);
1656
2033
  }, [form, path.key, depsKey]);
1657
- const onBlur = useStageFn(() => {
1658
- setTouchedByPath(form, path);
1659
- if (mode === "onBlur" || mode === "onTouched" || mode === "all" || liveErrors.length > 0 && form.reValidateMode === "onBlur")
1660
- validator();
1661
- });
2034
+ const elementRef = React.useRef(null);
2035
+ const focusRef = React.useCallback((el) => {
2036
+ elementRef.current = el;
2037
+ }, []);
2038
+ React.useEffect(
2039
+ () => on(
2040
+ form.emitter,
2041
+ "focusError",
2042
+ (key, options) => {
2043
+ if (key !== path.key) return;
2044
+ const el = elementRef.current;
2045
+ if (!el || typeof el.focus !== "function") return;
2046
+ el.focus();
2047
+ if (options?.shouldSelect && typeof el.select === "function") {
2048
+ el.select();
2049
+ }
2050
+ }
2051
+ ),
2052
+ [form, path.key]
2053
+ );
1662
2054
  React.useEffect(
1663
2055
  () => () => {
1664
- if (shouldUnregister !== false) {
2056
+ if ((shouldUnregister ?? form.shouldUnregister) !== false) {
1665
2057
  removeFieldByPath(form, path);
1666
2058
  }
1667
2059
  },
@@ -1676,7 +2068,8 @@
1676
2068
  onChange,
1677
2069
  onBlur,
1678
2070
  name: path.key,
1679
- disabled: formDisabled || !!disabled
2071
+ disabled: formDisabled || !!disabled,
2072
+ focusRef
1680
2073
  };
1681
2074
  }
1682
2075
  function useField(options) {
@@ -1905,29 +2298,72 @@
1905
2298
  return group;
1906
2299
  }
1907
2300
 
2301
+ function appendFormDataValue(fd, key, value) {
2302
+ if (value == null) return;
2303
+ if (Array.isArray(value)) {
2304
+ for (const item of value) appendFormDataValue(fd, key, item);
2305
+ return;
2306
+ }
2307
+ if (typeof FileList !== "undefined" && value instanceof FileList) {
2308
+ for (let i = 0; i < value.length; i++) fd.append(key, value.item(i));
2309
+ return;
2310
+ }
2311
+ if (typeof File !== "undefined" && value instanceof File) {
2312
+ fd.append(key, value, value.name);
2313
+ return;
2314
+ }
2315
+ if (typeof Blob !== "undefined" && value instanceof Blob) {
2316
+ fd.append(key, value);
2317
+ return;
2318
+ }
2319
+ if (value instanceof Date) {
2320
+ fd.append(key, value.toISOString());
2321
+ return;
2322
+ }
2323
+ fd.append(
2324
+ key,
2325
+ typeof value === "object" ? JSON.stringify(value) : String(value)
2326
+ );
2327
+ }
2328
+ function formDataFromValues(values) {
2329
+ const fd = new FormData();
2330
+ for (const key of Object.keys(values)) {
2331
+ appendFormDataValue(fd, key, values[key]);
2332
+ }
2333
+ return fd;
2334
+ }
2335
+
1908
2336
  function Form({
1909
2337
  form: f1,
1910
2338
  context,
1911
2339
  initialValues,
1912
2340
  values,
2341
+ shouldUnregister,
1913
2342
  onSubmit,
1914
2343
  onValidSubmit,
1915
2344
  onInvalidSubmit,
2345
+ action,
1916
2346
  shouldFocusError,
1917
2347
  ...props
1918
2348
  }) {
1919
- const f2 = useForm({ initialValues, values });
2349
+ const f2 = useForm({ initialValues, values, shouldUnregister });
1920
2350
  const form = f1 || f2;
1921
2351
  const submit = handleSubmit(form, {
1922
2352
  onSubmit,
1923
2353
  onValidSubmit,
1924
2354
  onInvalidSubmit,
1925
- shouldFocusError
2355
+ shouldFocusError,
2356
+ onAction: action ? (values2) => action(formDataFromValues(values2)) : void 0
1926
2357
  });
1927
2358
  const { Provider } = context ?? FormContext;
1928
2359
  return /* @__PURE__ */ React__namespace.createElement(Provider, { value: form }, /* @__PURE__ */ React__namespace.createElement("form", { ...props, noValidate: true, onSubmit: submit }));
1929
2360
  }
1930
2361
 
2362
+ function FormField({ children, ...options }) {
2363
+ const field = useField(options);
2364
+ return children(field);
2365
+ }
2366
+
1931
2367
  function setRef(ref, value) {
1932
2368
  if (typeof ref === "function") {
1933
2369
  ref(value);
@@ -1965,25 +2401,19 @@
1965
2401
  disabled,
1966
2402
  delayError,
1967
2403
  mode,
2404
+ uncontrolled,
1968
2405
  ...props
1969
2406
  }, ref) => {
1970
2407
  const innerRef = React__namespace.useRef(null);
1971
2408
  const [nativeInvalidCount, setNativeInvalidCount] = React__namespace.useState(0);
1972
- const mergedRef = React__namespace.useCallback(
1973
- (node) => {
1974
- innerRef.current = node;
1975
- setRef(ref, node);
1976
- },
1977
- [ref]
1978
- );
1979
2409
  const {
1980
2410
  value,
1981
2411
  onChange,
1982
2412
  onBlur,
1983
2413
  error,
1984
- form,
1985
2414
  name: fieldKey,
1986
- disabled: isDisabled
2415
+ disabled: isDisabled,
2416
+ focusRef
1987
2417
  } = useField({
1988
2418
  name,
1989
2419
  form: formProp,
@@ -1994,6 +2424,9 @@
1994
2424
  delayError,
1995
2425
  disabled,
1996
2426
  mode,
2427
+ // File inputs cannot be value-controlled at all — force the
2428
+ // uncontrolled path so no `value` prop ever reaches the element.
2429
+ uncontrolled: uncontrolled || props.type === "file",
1997
2430
  validate: (...params) => {
1998
2431
  const el = innerRef.current;
1999
2432
  if (el && typeof el.checkValidity === "function") {
@@ -2006,6 +2439,14 @@
2006
2439
  if (validate) return validate(...params);
2007
2440
  }
2008
2441
  });
2442
+ const mergedRef = React__namespace.useCallback(
2443
+ (node) => {
2444
+ innerRef.current = node;
2445
+ focusRef(node);
2446
+ setRef(ref, node);
2447
+ },
2448
+ [ref, focusRef]
2449
+ );
2009
2450
  const Component = as || "input";
2010
2451
  React__namespace.useEffect(() => {
2011
2452
  const el = innerRef.current;
@@ -2020,23 +2461,9 @@
2020
2461
  React__namespace.useEffect(() => {
2021
2462
  if (nativeInvalidCount > 0) innerRef.current?.reportValidity();
2022
2463
  }, [nativeInvalidCount]);
2023
- React__namespace.useEffect(
2024
- () => on(
2025
- form.emitter,
2026
- "focusError",
2027
- (key, options) => {
2028
- if (key !== fieldKey) return;
2029
- const el = innerRef.current;
2030
- if (!el || typeof el.focus !== "function") return;
2031
- el.focus();
2032
- if (options?.shouldSelect && typeof el.select === "function") {
2033
- el.select();
2034
- }
2035
- }
2036
- ),
2037
- [form, fieldKey]
2038
- );
2039
- const toValue = eventToValue ?? ((e) => e.target.value);
2464
+ const isFile = props.type === "file";
2465
+ const toValue = eventToValue ?? (isFile ? (e) => e.target.files : (e) => e.target.value);
2466
+ const valueProps = valueToProps ? valueToProps(value) : isFile ? {} : uncontrolled ? { defaultValue: value } : { value };
2040
2467
  const errorId = errorIdFromKey(fieldKey);
2041
2468
  return /* @__PURE__ */ React__namespace.createElement(React__namespace.Fragment, null, /* @__PURE__ */ React__namespace.createElement(
2042
2469
  Component,
@@ -2045,7 +2472,7 @@
2045
2472
  name: fieldKey,
2046
2473
  onBlur,
2047
2474
  ...asProps,
2048
- ...valueToProps ? valueToProps(value) : { value },
2475
+ ...valueProps,
2049
2476
  ...ariaProps(error, fieldKey, props),
2050
2477
  disabled: isDisabled,
2051
2478
  onChange: (e) => onChange(toValue(e)),
@@ -2165,9 +2592,11 @@
2165
2592
  exports.Checkbox = Checkbox;
2166
2593
  exports.CheckboxGroupContext = CheckboxGroupContext;
2167
2594
  exports.CheckboxGroupProvider = CheckboxGroupProvider;
2595
+ exports.FORM_ERROR = FORM_ERROR;
2168
2596
  exports.Field = Field;
2169
2597
  exports.Form = Form;
2170
2598
  exports.FormContext = FormContext;
2599
+ exports.FormField = FormField;
2171
2600
  exports.FormProvider = FormProvider;
2172
2601
  exports.Select = Select;
2173
2602
  exports.VALIDATION_OUTCOME = VALIDATION_OUTCOME;
@@ -2198,7 +2627,9 @@
2198
2627
  exports.incrementSubmitCount = incrementSubmitCount;
2199
2628
  exports.isDirty = isDirty;
2200
2629
  exports.isTouched = isTouched;
2630
+ exports.registerFieldMode = registerFieldMode;
2201
2631
  exports.registerFieldValidateDeps = registerFieldValidateDeps;
2632
+ exports.registerValidatorByPath = registerValidatorByPath;
2202
2633
  exports.removeField = removeField;
2203
2634
  exports.removeFieldByPath = removeFieldByPath;
2204
2635
  exports.reset = reset;
@@ -2221,6 +2652,7 @@
2221
2652
  exports.setValueByPath = setValueByPath;
2222
2653
  exports.subscribe = subscribe;
2223
2654
  exports.trigger = trigger;
2655
+ exports.unregisterFieldMode = unregisterFieldMode;
2224
2656
  exports.unregisterFieldValidateDeps = unregisterFieldValidateDeps;
2225
2657
  exports.unsetValidatingByPath = unsetValidatingByPath;
2226
2658
  exports.useCanSubmit = useCanSubmit;
@@ -2235,9 +2667,16 @@
2235
2667
  exports.useFieldErrorsByPath = useFieldErrorsByPath;
2236
2668
  exports.useForm = useForm;
2237
2669
  exports.useFormContext = useFormContext;
2670
+ exports.useFormError = useFormError;
2671
+ exports.useFormErrors = useFormErrors;
2672
+ exports.useFormState = useFormState;
2238
2673
  exports.useHasErrors = useHasErrors;
2239
2674
  exports.useIsDirty = useIsDirty;
2675
+ exports.useIsLoading = useIsLoading;
2676
+ exports.useIsSubmitSuccessful = useIsSubmitSuccessful;
2240
2677
  exports.useIsSubmitting = useIsSubmitting;
2678
+ exports.useIsValid = useIsValid;
2679
+ exports.useIsValidating = useIsValidating;
2241
2680
  exports.useSubmitCount = useSubmitCount;
2242
2681
  exports.useTouched = useTouched;
2243
2682
  exports.useTouchedByPath = useTouchedByPath;
@@ -2245,6 +2684,9 @@
2245
2684
  exports.useValue = useValue;
2246
2685
  exports.useValueByPath = useValueByPath;
2247
2686
  exports.useWatch = useWatch;
2687
+ exports.useWatchCore = useWatchCore;
2688
+ exports.userBlur = userBlur;
2689
+ exports.userChangeByPath = userChangeByPath;
2248
2690
  exports.validate = validate;
2249
2691
 
2250
2692
  }));