react-f0rm 1.1.0 → 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 +801 -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-BGWPwts2.mjs +0 -2
  57. package/dist/form-BGWPwts2.mjs.map +0 -1
  58. package/dist/form-BiDaJLjD.d.ts +0 -826
  59. package/dist/form-DwuY91QB.cjs.js +0 -2
  60. package/dist/form-DwuY91QB.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,31 +91,101 @@
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
  };
50
176
  }
51
177
 
178
+ const PATH_CACHE_LIMIT = 1e4;
52
179
  const pathCache = /* @__PURE__ */ new Map();
53
180
  function normalizePath(path) {
54
181
  if (Array.isArray(path)) return path;
55
182
  const cached = pathCache.get(path);
56
183
  if (cached) return cached;
57
184
  const value = parsePath(path);
185
+ if (pathCache.size >= PATH_CACHE_LIMIT) {
186
+ const oldest = pathCache.keys().next();
187
+ if (!oldest.done) pathCache.delete(oldest.value);
188
+ }
58
189
  pathCache.set(path, value);
59
190
  return value;
60
191
  }
@@ -225,110 +356,59 @@
225
356
  return { value, key: JSON.stringify(value) };
226
357
  }
227
358
 
228
- const VALIDATION_OUTCOME = /* @__PURE__ */ Symbol("validation-outcome");
229
- function create(options) {
230
- const emitter = create$2();
231
- return {
232
- emitter,
233
- ...options,
234
- mode: options?.mode ?? "onSubmit",
235
- reValidateMode: options?.reValidateMode ?? "onChange",
236
- disabled: options?.disabled ?? false,
237
- validateDeps: options?.validateDeps ? new Set(options.validateDeps.map((dep) => create$1(dep).key)) : void 0,
238
- initialValues: options?.initialValues ?? {},
239
- values: /* @__PURE__ */ new Map(),
240
- deleted: /* @__PURE__ */ new Set(),
241
- errors: /* @__PURE__ */ new Map(),
242
- touched: /* @__PURE__ */ new Set(),
243
- validators: /* @__PURE__ */ new Map(),
244
- changeHandlers: /* @__PURE__ */ new Map(),
245
- validating: /* @__PURE__ */ new Set(),
246
- parsedValues: void 0,
247
- isSubmitting: false,
248
- submitCount: 0,
249
- isSubmitSuccessful: void 0
250
- };
251
- }
252
359
  const valuesCaches = /* @__PURE__ */ new WeakMap();
253
360
  function bumpValuesVersion(form) {
254
361
  const cache = valuesCaches.get(form);
255
362
  if (cache) cache.version++;
256
363
  }
257
- function getValues(form) {
258
- let cache = valuesCaches.get(form);
259
- if (!cache) {
260
- cache = { version: 0, result: computeValues(form) };
261
- valuesCaches.set(form, cache);
262
- } else if (cache.version > 0) {
263
- cache.result = computeValues(form);
264
- cache.version = 0;
265
- }
266
- 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);
267
369
  }
268
- function computeValues(form) {
269
- const { initialValues, parsedValues, values, deleted } = form;
270
- const owned = /* @__PURE__ */ new Set();
271
- let merged = parsedValues ?? initialValues;
272
- for (const [key, value] of values) {
273
- merged = setOwned(merged, JSON.parse(key), value, owned);
274
- }
275
- for (const key of deleted) {
276
- 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);
277
375
  }
278
- return merged;
279
- }
280
- function getValue(form, name) {
281
- return getValueByPath(form, create$1(name));
376
+ baselines.set(key, value);
282
377
  }
283
- function getValueByPath({ initialValues, parsedValues, values, deleted }, path) {
284
- const { key, value: segments } = path;
285
- if (values.has(key)) return values.get(key);
286
- if (deleted.has(key)) return void 0;
287
- for (let i = segments.length - 1; i > 0; i--) {
288
- const ancestorKey = JSON.stringify(segments.slice(0, i));
289
- if (values.has(ancestorKey)) {
290
- return get(values.get(ancestorKey), segments.slice(i));
291
- }
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);
292
384
  }
293
- return get(parsedValues ?? initialValues, segments);
294
385
  }
295
- function setValue(form, name, value, options) {
296
- setValueByPath(form, create$1(name), value, options);
297
- }
298
- function setValueByPath(form, path, value, options) {
299
- const { emitter, values, deleted } = form;
300
- values.set(path.key, value);
301
- pruneDescendantKeys(values, path);
302
- reviveBranch(deleted, path);
303
- pruneDirtyBaselines(form, path);
304
- if (options?.shouldDirty === false) setDirtyBaseline(form, path, value);
305
- bumpDirtyVersion(form);
306
- bumpValuesVersion(form);
307
- if (options?.shouldTouch) setTouchedByPath(form, path);
308
- if (options?.shouldValidate) form.validators.get(path.key)?.();
309
- 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);
310
391
  }
311
- function seedValueByPath(form, path, value) {
312
- const { values, deleted } = form;
313
- values.set(path.key, value);
314
- pruneDescendantKeys(values, path);
315
- reviveBranch(deleted, path);
316
- pruneDirtyBaselines(form, path);
317
- bumpDirtyVersion(form);
318
- bumpValuesVersion(form);
392
+ const dirtyFieldsCaches = /* @__PURE__ */ new WeakMap();
393
+ function bumpDirtyVersion(form) {
394
+ const cache = dirtyFieldsCaches.get(form);
395
+ if (cache) cache.version++;
319
396
  }
320
- function emitChangeByPath({ emitter }, path) {
321
- emit(emitter, "change", path);
397
+ function isSegmentsPath(name) {
398
+ return name.some((part) => typeof part === "number");
322
399
  }
323
- function changeValue(form, name, value, options) {
324
- 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";
325
402
  }
326
- function changeValueByPath(form, path, value, options) {
327
- if (options?.shouldDirty === false) setDirtyBaseline(form, path, value);
328
- const change = form.changeHandlers.get(path.key);
329
- if (change) change(value);
330
- 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");
331
408
  }
409
+
410
+ const FORM_ERROR = "_form";
411
+ const VALIDATION_OUTCOME = /* @__PURE__ */ Symbol("validation-outcome");
332
412
  function getError(form, name) {
333
413
  return getErrorByPath(form, create$1(name));
334
414
  }
@@ -353,29 +433,6 @@
353
433
  function getFirstError({ errors }) {
354
434
  return errors.values().next().value?.[0]?.message;
355
435
  }
356
- function getFieldState(form, name) {
357
- const path = create$1(name);
358
- const { values, touched, validating } = form;
359
- const live = values.get(path.key);
360
- return {
361
- value: getValueByPath(form, path),
362
- error: getErrorByPath(form, path),
363
- errors: getFieldErrorsByPath(form, path),
364
- // Same rule as getDirtyFields, committed baselines included: the field
365
- // is dirty while its live value differs from its effective baseline.
366
- isDirty: values.has(path.key) && getDirtyBaseline(form, path.key, path.value) !== live,
367
- isTouched: touched.has(path.key),
368
- isValidating: validating.has(path.key)
369
- };
370
- }
371
- function unsetValidatingByPath({ emitter, validating }, path) {
372
- validating.delete(path.key);
373
- emit(emitter, "validating", path);
374
- }
375
- function setValidatingByPath({ emitter, validating }, path) {
376
- validating.add(path.key);
377
- emit(emitter, "validating", path);
378
- }
379
436
  function setError(form, name, error) {
380
437
  setErrorByPath(form, create$1(name), error);
381
438
  }
@@ -425,6 +482,10 @@
425
482
  );
426
483
  }
427
484
  }
485
+ function hasErrors({ errors }) {
486
+ return errors.size > 0;
487
+ }
488
+
428
489
  function setTouched(form, name) {
429
490
  setTouchedByPath(form, create$1(name));
430
491
  }
@@ -439,97 +500,111 @@
439
500
  function hasTouchedByPath({ touched }, path) {
440
501
  return touched.has(path.key);
441
502
  }
442
- function isDirty(form) {
443
- let dirty = false;
444
- forEachDirtyField(form, () => {
445
- dirty = true;
446
- });
447
- return dirty;
448
- }
449
- function forEachDirtyField(form, fn) {
450
- for (const [key, value] of form.values) {
451
- const path = JSON.parse(key);
452
- if (getDirtyBaseline(form, key, path) !== value) fn(path.join("."));
453
- }
503
+ function getTouchedFields({ touched }) {
504
+ return Array.from(
505
+ touched,
506
+ (key) => JSON.parse(key).join(".")
507
+ );
454
508
  }
455
- const dirtyBaselines = /* @__PURE__ */ new WeakMap();
456
- function getDirtyBaseline(form, key, segments) {
457
- const baselines = dirtyBaselines.get(form);
458
- if (baselines?.has(key)) return baselines.get(key);
459
- return get(form.initialValues, segments);
509
+ function isTouched({ touched }) {
510
+ return touched.size > 0;
460
511
  }
461
- function setDirtyBaseline(form, { key }, value) {
462
- let baselines = dirtyBaselines.get(form);
463
- if (!baselines) {
464
- baselines = /* @__PURE__ */ new Map();
465
- 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;
466
521
  }
467
- baselines.set(key, value);
522
+ return cache.result;
468
523
  }
469
- function pruneDirtyBaselines(form, { key }) {
470
- const baselines = dirtyBaselines.get(form);
471
- if (!baselines?.size) return;
472
- const stem = `${key.slice(0, -1)},`;
473
- for (const baselineKey of baselines.keys()) {
474
- 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);
475
530
  }
531
+ for (const key of deleted) {
532
+ merged = unset(merged, JSON.parse(key));
533
+ }
534
+ return merged;
476
535
  }
477
- function clearDirtyBaselines(form, key) {
478
- const baselines = dirtyBaselines.get(form);
479
- if (!baselines) return;
480
- if (key === void 0) baselines.clear();
481
- else baselines.delete(key);
536
+ function getValue(form, name) {
537
+ return getValueByPath(form, create$1(name));
482
538
  }
483
- const dirtyFieldsCaches = /* @__PURE__ */ new WeakMap();
484
- function bumpDirtyVersion(form) {
485
- const cache = dirtyFieldsCaches.get(form);
486
- 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);
487
550
  }
488
- function computeDirtyFields(form) {
489
- const dirtyFields = {};
490
- forEachDirtyField(form, (key) => {
491
- dirtyFields[key] = true;
492
- });
493
- return dirtyFields;
551
+ function setValue(form, name, value, options) {
552
+ setValueByPath(form, create$1(name), value, options);
494
553
  }
495
- function sameDirtyKeys(a, b) {
496
- const aKeys = Object.keys(a);
497
- if (aKeys.length !== Object.keys(b).length) return false;
498
- 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);
499
566
  }
500
- function getDirtyFields(form) {
501
- let cache = dirtyFieldsCaches.get(form);
502
- if (!cache) {
503
- cache = { version: 0, result: computeDirtyFields(form) };
504
- dirtyFieldsCaches.set(form, cache);
505
- } else if (cache.version > 0) {
506
- const result = computeDirtyFields(form);
507
- if (!sameDirtyKeys(cache.result, result)) cache.result = result;
508
- cache.version = 0;
509
- }
510
- 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);
511
575
  }
512
- function getTouchedFields({ touched }) {
513
- return Array.from(
514
- touched,
515
- (key) => JSON.parse(key).join(".")
516
- );
576
+ function emitChangeByPath({ emitter }, path) {
577
+ emit(emitter, "change", path);
517
578
  }
518
- function isTouched({ touched }) {
519
- 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
+ };
520
593
  }
521
- function removeField(form, name) {
522
- removeFieldByPath(form, create$1(name));
594
+ function removeField(form, name, options) {
595
+ removeFieldByPath(form, create$1(name), options);
523
596
  }
524
- function removeFieldByPath(form, path) {
597
+ function removeFieldByPath(form, path, options) {
525
598
  const { key, value: segments } = path;
526
599
  const { emitter, values, touched, errors, validating, deleted } = form;
527
- values.delete(key);
528
- touched.delete(key);
529
- 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);
530
607
  validating.delete(key);
531
- clearDirtyBaselines(form, key);
532
- if (!hasLiveBranch(values, segments)) deleted.add(key);
533
608
  bumpDirtyVersion(form);
534
609
  bumpValuesVersion(form);
535
610
  emit(emitter, "change", path);
@@ -575,17 +650,37 @@
575
650
  bumpValuesVersion(form);
576
651
  emit(form.emitter, "change");
577
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
+ }
578
671
  function reset(form, initialValues, options) {
579
- const dirtyValues = [];
580
- if (options?.keepDirtyValues) {
672
+ const keptValues = [];
673
+ if (options?.keepValues) {
674
+ collectValueLeaves(getValues(form), [], keptValues);
675
+ } else if (options?.keepDirtyValues) {
581
676
  for (const [key, value] of form.values) {
582
677
  const segments = JSON.parse(key);
583
678
  if (getDirtyBaseline(form, key, segments) !== value) {
584
- dirtyValues.push({ segments, value });
679
+ keptValues.push({ segments, value });
585
680
  }
586
681
  }
587
682
  }
588
- form.initialValues = initialValues;
683
+ form.initialValues = options?.keepDefaultValues ? form.initialValues : initialValues ?? form.initialValues;
589
684
  form.parsedValues = void 0;
590
685
  if (!options?.keepErrors) clearErrors(form);
591
686
  const { emitter, touched, values, deleted, validating } = form;
@@ -599,7 +694,7 @@
599
694
  if (!options?.keepIsSubmitted) form.isSubmitSuccessful = void 0;
600
695
  bumpDirtyVersion(form);
601
696
  bumpValuesVersion(form);
602
- for (const { segments, value } of dirtyValues) {
697
+ for (const { segments, value } of keptValues) {
603
698
  setValueByPath(form, create$1(segments), value);
604
699
  }
605
700
  emit(emitter, "change");
@@ -634,10 +729,172 @@
634
729
  bumpDirtyVersion(form);
635
730
  bumpValuesVersion(form);
636
731
  }
637
- function hasErrors({ errors }) {
638
- 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
+ };
639
896
  }
640
- async function trigger(form, name) {
897
+ async function trigger(form, name, options) {
641
898
  const settle = (keys2) => waitUntil(
642
899
  form.emitter,
643
900
  "validating",
@@ -648,18 +905,25 @@
648
905
  form.validators.forEach((validator) => validator());
649
906
  await settle();
650
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
+ }
651
913
  return !hasErrors(form);
652
914
  }
653
915
  const keys = typeof name === "string" || isSegmentsPath(name) ? [create$1(name).key] : name.map((one) => create$1(one).key);
654
916
  keys.forEach((key) => form.validators.get(key)?.());
655
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
+ }
656
923
  return keys.every((key) => !form.errors.has(key));
657
924
  }
658
- function isSegmentsPath(name) {
659
- return name.some((part) => typeof part === "number");
660
- }
661
- function isFieldError(value) {
662
- 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))));
663
927
  }
664
928
  function setFormErrors(form, result, segments = [], footprint) {
665
929
  Object.entries(result).forEach(([key, value]) => {
@@ -689,12 +953,6 @@
689
953
  const stored = form.errors.get(path.key);
690
954
  if (stored) footprint.set(path.key, stored);
691
955
  }
692
- function setParsedValues(form, values) {
693
- if (values === void 0 || values === form.parsedValues) return;
694
- form.parsedValues = values;
695
- bumpValuesVersion(form);
696
- emit(form.emitter, "change");
697
- }
698
956
  function applyValidateResult(form, result) {
699
957
  const footprint = form.validateDeps ? getFormErrorFootprint(form) : void 0;
700
958
  if (footprint) {
@@ -892,6 +1150,55 @@
892
1150
  async function validate(form) {
893
1151
  return ensureValidate(form).catch((e) => e.message);
894
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
+
895
1202
  function setIsSubmitting(form, value) {
896
1203
  form.isSubmitting = value;
897
1204
  emit(form.emitter, "submitting");
@@ -938,6 +1245,7 @@
938
1245
  onSubmit,
939
1246
  onValidSubmit,
940
1247
  onInvalidSubmit,
1248
+ onAction,
941
1249
  shouldFocusError = true
942
1250
  } = options ?? {};
943
1251
  return async (e) => {
@@ -974,6 +1282,7 @@
974
1282
  const submitted = getValues(form);
975
1283
  if (onSubmit) await onSubmit(submitted, e);
976
1284
  if (onValidSubmit) await onValidSubmit(submitted, e);
1285
+ if (onAction) await onAction(submitted, e);
977
1286
  setSubmitSuccessful(form, true);
978
1287
  } catch {
979
1288
  setSubmitSuccessful(form, false);
@@ -982,12 +1291,58 @@
982
1291
  }
983
1292
  };
984
1293
  }
1294
+
985
1295
  function setFocus(form, name, options) {
986
1296
  const { key } = create$1(name);
987
1297
  if (options) emit(form.emitter, "focusError", key, options);
988
1298
  else emit(form.emitter, "focusError", key);
989
1299
  }
990
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
+
991
1346
  function defaultMessage(type, bound) {
992
1347
  switch (type) {
993
1348
  case "required":
@@ -1297,6 +1652,9 @@
1297
1652
  seededRef.current = { done: false, source: void 0 };
1298
1653
  React.useEffect(() => {
1299
1654
  if (initialValues === void 0) return;
1655
+ if (typeof initialValues === "function" || isPromise(initialValues)) {
1656
+ return;
1657
+ }
1300
1658
  const seeded = seededRef.current;
1301
1659
  if (seeded.done && (seeded.source === initialValues || isEqual(seeded.source, initialValues))) {
1302
1660
  return;
@@ -1321,12 +1679,14 @@
1321
1679
  }, [form, values]);
1322
1680
  return form;
1323
1681
  }
1324
- function useWatchCore(subscribeFactory, getter) {
1682
+ function useWatchCore(subscribeFactory, getter, isEqual2) {
1325
1683
  const cacheRef = React.useRef(null);
1326
1684
  if (cacheRef.current === null) cacheRef.current = { hasValue: false };
1327
1685
  const cache = cacheRef.current;
1328
1686
  const getterRef = React.useRef(getter);
1329
1687
  getterRef.current = getter;
1688
+ const isEqualRef = React.useRef(isEqual2);
1689
+ isEqualRef.current = isEqual2;
1330
1690
  const getSnapshot = React.useCallback(() => {
1331
1691
  if (!cache.hasValue) {
1332
1692
  cache.value = getterRef.current();
@@ -1338,6 +1698,14 @@
1338
1698
  (notify) => {
1339
1699
  cache.hasValue = false;
1340
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
+ }
1341
1709
  cache.hasValue = false;
1342
1710
  notify();
1343
1711
  };
@@ -1347,12 +1715,13 @@
1347
1715
  );
1348
1716
  return shimExports.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
1349
1717
  }
1350
- function useWatch(emitter, event, getter) {
1718
+ function useWatch(formOrEmitter, event, getter, isEqual2) {
1719
+ const emitter = "emitter" in formOrEmitter ? formOrEmitter.emitter : formOrEmitter;
1351
1720
  const subscribeFactory = React.useCallback(
1352
1721
  (invalidate) => on(emitter, event, invalidate),
1353
1722
  [emitter, event]
1354
1723
  );
1355
- return useWatchCore(subscribeFactory, getter);
1724
+ return useWatchCore(subscribeFactory, getter, isEqual2);
1356
1725
  }
1357
1726
  function useValue(form, name) {
1358
1727
  return useValueByPath(form, create$1(name));
@@ -1410,19 +1779,71 @@
1410
1779
  );
1411
1780
  }
1412
1781
  function useIsDirty(form) {
1413
- return useWatch(form.emitter, "change", isDirty.bind(null, form));
1782
+ return useWatch(form, "change", isDirty.bind(null, form));
1414
1783
  }
1415
1784
  function useDirtyFields(form) {
1416
- return useWatch(form.emitter, "change", getDirtyFields.bind(null, form));
1785
+ return useWatch(form, "change", getDirtyFields.bind(null, form));
1417
1786
  }
1418
1787
  function useTouchedFields(form) {
1419
- 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);
1420
1835
  }
1421
1836
  function useHasErrors(form) {
1422
- 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));
1423
1841
  }
1424
1842
  function useIsSubmitting(form) {
1425
- 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);
1426
1847
  }
1427
1848
  function useCanSubmit(form) {
1428
1849
  const { emitter } = form;
@@ -1443,7 +1864,23 @@
1443
1864
  );
1444
1865
  }
1445
1866
  function useSubmitCount(form) {
1446
- 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));
1447
1884
  }
1448
1885
 
1449
1886
  function usePath(name) {
@@ -1468,92 +1905,29 @@
1468
1905
  const contextForm = React.useContext(FormContext);
1469
1906
  const form = formProp || contextForm;
1470
1907
  if (!form) throw new Error("no form provided");
1471
- const lockRef = React.useRef(null);
1472
1908
  const validateRef = React.useRef(validate);
1473
1909
  validateRef.current = validate;
1474
1910
  const debounceRef = React.useRef(options?.debounce ?? 0);
1475
1911
  debounceRef.current = options?.debounce ?? 0;
1476
- React.useEffect(() => {
1477
- let timer = null;
1478
- let controller = null;
1479
- let marked = false;
1480
- const mark = () => {
1481
- if (marked) return;
1482
- marked = true;
1483
- setValidatingByPath(form, path);
1484
- };
1485
- const unmark = () => {
1486
- if (!marked) return;
1487
- marked = false;
1488
- unsetValidatingByPath(form, path);
1489
- };
1490
- const run = () => {
1491
- timer = null;
1492
- const fn = validateRef.current;
1493
- if (!fn) {
1494
- unmark();
1495
- return;
1496
- }
1497
- controller?.abort();
1498
- controller = new AbortController();
1499
- const lock = lockRef.current = {};
1500
- let result;
1501
- try {
1502
- result = fn(getValueByPath(form, path), {
1503
- form,
1504
- path,
1505
- signal: controller.signal
1506
- });
1507
- } catch (e) {
1508
- unmark();
1509
- throw e;
1510
- }
1511
- if (!isPromise(result)) {
1512
- setErrorByPath(form, path, result);
1513
- unmark();
1514
- return;
1515
- }
1516
- mark();
1517
- result.then(
1518
- (error) => {
1519
- if (lock === lockRef.current) {
1520
- setErrorByPath(form, path, error);
1521
- }
1522
- }
1523
- ).catch(() => {
1524
- }).finally(() => {
1525
- if (lock === lockRef.current) {
1526
- unmark();
1527
- lockRef.current = null;
1528
- }
1529
- });
1530
- };
1531
- const validator = () => {
1532
- const debounce = debounceRef.current;
1533
- if (debounce > 0) {
1534
- if (timer !== null) clearTimeout(timer);
1535
- else mark();
1536
- timer = setTimeout(run, debounce);
1537
- return;
1538
- }
1539
- run();
1540
- };
1541
- form.validators.set(path.key, validator);
1542
- return () => {
1543
- form.validators.delete(path.key);
1544
- if (timer !== null) {
1545
- clearTimeout(timer);
1546
- timer = null;
1547
- }
1548
- unmark();
1549
- controller?.abort();
1550
- };
1551
- }, [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
+ );
1552
1923
  return useStageFn(() => form.validators.get(path.key)?.());
1553
1924
  }
1554
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
+ }
1555
1929
  function combineRulesAndValidate(rules, validate) {
1556
- if (!rules) return validate;
1930
+ if (!rules || !hasRuleConstraints(rules)) return validate;
1557
1931
  const ruleValidator = rulesToValidator(rules);
1558
1932
  if (!validate) return ruleValidator;
1559
1933
  return (value, meta) => {
@@ -1585,6 +1959,25 @@
1585
1959
  }, [errors, delay, shown]);
1586
1960
  return delay === void 0 ? errors : shown;
1587
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
+ }
1588
1981
  function useFieldCore({
1589
1982
  form: f1,
1590
1983
  name,
@@ -1596,6 +1989,7 @@
1596
1989
  validateDeps,
1597
1990
  delayError,
1598
1991
  disabled,
1992
+ uncontrolled,
1599
1993
  mode: modeOption
1600
1994
  }, Context) {
1601
1995
  const contextForm = React.useContext(Context);
@@ -1613,35 +2007,23 @@
1613
2007
  seededRef.current = false;
1614
2008
  emitChangeByPath(form, path);
1615
2009
  });
1616
- const validator = useValidate(
1617
- combineRulesAndValidate(rules, validate),
1618
- path,
1619
- form,
1620
- {
1621
- debounce: validateDebounce
1622
- }
1623
- );
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
+ });
1624
2015
  const liveErrors = useFieldErrorsByPath(form, path);
1625
2016
  const errors = useDelayedErrors(liveErrors, delayError);
1626
2017
  const errorObject = errors[0];
1627
2018
  const error = errorObject?.message;
1628
- const value = useValueByPath(form, path);
1629
- const formDisabled = useWatch(form.emitter, "disabled", () => form.disabled);
1630
- const mode = modeOption ?? form.mode;
1631
- const onChange = useStageFn((v) => {
1632
- setValueByPath(form, path, v);
1633
- if (mode === "onChange" || mode === "all" || mode === "onTouched" && hasTouchedByPath(form, path) || liveErrors.length > 0 && form.reValidateMode === "onChange")
1634
- validator();
1635
- revalidateFormOnChange(form, path, mode);
1636
- revalidateDependentsOnChange(form, path, mode);
1637
- });
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));
1638
2023
  React.useEffect(() => {
1639
- form.changeHandlers.set(path.key, onChange);
1640
- return () => {
1641
- if (form.changeHandlers.get(path.key) === onChange)
1642
- form.changeHandlers.delete(path.key);
1643
- };
1644
- }, [form, path.key, onChange]);
2024
+ const { token, displaced } = registerFieldMode(form, path, modeOption);
2025
+ return () => unregisterFieldMode(form, path, token);
2026
+ }, [form, path.key, modeOption]);
1645
2027
  const depsKey = validateDeps?.length ? validateDeps.join("\n") : void 0;
1646
2028
  React.useEffect(() => {
1647
2029
  if (!depsKey) return;
@@ -1649,14 +2031,29 @@
1649
2031
  registerFieldValidateDeps(form, path.key, depKeys);
1650
2032
  return () => unregisterFieldValidateDeps(form, path.key, depKeys);
1651
2033
  }, [form, path.key, depsKey]);
1652
- const onBlur = useStageFn(() => {
1653
- setTouchedByPath(form, path);
1654
- if (mode === "onBlur" || mode === "onTouched" || mode === "all" || liveErrors.length > 0 && form.reValidateMode === "onBlur")
1655
- validator();
1656
- });
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
+ );
1657
2054
  React.useEffect(
1658
2055
  () => () => {
1659
- if (shouldUnregister !== false) {
2056
+ if ((shouldUnregister ?? form.shouldUnregister) !== false) {
1660
2057
  removeFieldByPath(form, path);
1661
2058
  }
1662
2059
  },
@@ -1671,7 +2068,8 @@
1671
2068
  onChange,
1672
2069
  onBlur,
1673
2070
  name: path.key,
1674
- disabled: formDisabled || !!disabled
2071
+ disabled: formDisabled || !!disabled,
2072
+ focusRef
1675
2073
  };
1676
2074
  }
1677
2075
  function useField(options) {
@@ -1900,29 +2298,72 @@
1900
2298
  return group;
1901
2299
  }
1902
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
+
1903
2336
  function Form({
1904
2337
  form: f1,
1905
2338
  context,
1906
2339
  initialValues,
1907
2340
  values,
2341
+ shouldUnregister,
1908
2342
  onSubmit,
1909
2343
  onValidSubmit,
1910
2344
  onInvalidSubmit,
2345
+ action,
1911
2346
  shouldFocusError,
1912
2347
  ...props
1913
2348
  }) {
1914
- const f2 = useForm({ initialValues, values });
2349
+ const f2 = useForm({ initialValues, values, shouldUnregister });
1915
2350
  const form = f1 || f2;
1916
2351
  const submit = handleSubmit(form, {
1917
2352
  onSubmit,
1918
2353
  onValidSubmit,
1919
2354
  onInvalidSubmit,
1920
- shouldFocusError
2355
+ shouldFocusError,
2356
+ onAction: action ? (values2) => action(formDataFromValues(values2)) : void 0
1921
2357
  });
1922
2358
  const { Provider } = context ?? FormContext;
1923
2359
  return /* @__PURE__ */ React__namespace.createElement(Provider, { value: form }, /* @__PURE__ */ React__namespace.createElement("form", { ...props, noValidate: true, onSubmit: submit }));
1924
2360
  }
1925
2361
 
2362
+ function FormField({ children, ...options }) {
2363
+ const field = useField(options);
2364
+ return children(field);
2365
+ }
2366
+
1926
2367
  function setRef(ref, value) {
1927
2368
  if (typeof ref === "function") {
1928
2369
  ref(value);
@@ -1960,25 +2401,19 @@
1960
2401
  disabled,
1961
2402
  delayError,
1962
2403
  mode,
2404
+ uncontrolled,
1963
2405
  ...props
1964
2406
  }, ref) => {
1965
2407
  const innerRef = React__namespace.useRef(null);
1966
2408
  const [nativeInvalidCount, setNativeInvalidCount] = React__namespace.useState(0);
1967
- const mergedRef = React__namespace.useCallback(
1968
- (node) => {
1969
- innerRef.current = node;
1970
- setRef(ref, node);
1971
- },
1972
- [ref]
1973
- );
1974
2409
  const {
1975
2410
  value,
1976
2411
  onChange,
1977
2412
  onBlur,
1978
2413
  error,
1979
- form,
1980
2414
  name: fieldKey,
1981
- disabled: isDisabled
2415
+ disabled: isDisabled,
2416
+ focusRef
1982
2417
  } = useField({
1983
2418
  name,
1984
2419
  form: formProp,
@@ -1989,6 +2424,9 @@
1989
2424
  delayError,
1990
2425
  disabled,
1991
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",
1992
2430
  validate: (...params) => {
1993
2431
  const el = innerRef.current;
1994
2432
  if (el && typeof el.checkValidity === "function") {
@@ -2001,6 +2439,14 @@
2001
2439
  if (validate) return validate(...params);
2002
2440
  }
2003
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
+ );
2004
2450
  const Component = as || "input";
2005
2451
  React__namespace.useEffect(() => {
2006
2452
  const el = innerRef.current;
@@ -2015,23 +2461,9 @@
2015
2461
  React__namespace.useEffect(() => {
2016
2462
  if (nativeInvalidCount > 0) innerRef.current?.reportValidity();
2017
2463
  }, [nativeInvalidCount]);
2018
- React__namespace.useEffect(
2019
- () => on(
2020
- form.emitter,
2021
- "focusError",
2022
- (key, options) => {
2023
- if (key !== fieldKey) return;
2024
- const el = innerRef.current;
2025
- if (!el || typeof el.focus !== "function") return;
2026
- el.focus();
2027
- if (options?.shouldSelect && typeof el.select === "function") {
2028
- el.select();
2029
- }
2030
- }
2031
- ),
2032
- [form, fieldKey]
2033
- );
2034
- 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 };
2035
2467
  const errorId = errorIdFromKey(fieldKey);
2036
2468
  return /* @__PURE__ */ React__namespace.createElement(React__namespace.Fragment, null, /* @__PURE__ */ React__namespace.createElement(
2037
2469
  Component,
@@ -2040,7 +2472,7 @@
2040
2472
  name: fieldKey,
2041
2473
  onBlur,
2042
2474
  ...asProps,
2043
- ...valueToProps ? valueToProps(value) : { value },
2475
+ ...valueProps,
2044
2476
  ...ariaProps(error, fieldKey, props),
2045
2477
  disabled: isDisabled,
2046
2478
  onChange: (e) => onChange(toValue(e)),
@@ -2160,9 +2592,11 @@
2160
2592
  exports.Checkbox = Checkbox;
2161
2593
  exports.CheckboxGroupContext = CheckboxGroupContext;
2162
2594
  exports.CheckboxGroupProvider = CheckboxGroupProvider;
2595
+ exports.FORM_ERROR = FORM_ERROR;
2163
2596
  exports.Field = Field;
2164
2597
  exports.Form = Form;
2165
2598
  exports.FormContext = FormContext;
2599
+ exports.FormField = FormField;
2166
2600
  exports.FormProvider = FormProvider;
2167
2601
  exports.Select = Select;
2168
2602
  exports.VALIDATION_OUTCOME = VALIDATION_OUTCOME;
@@ -2193,7 +2627,9 @@
2193
2627
  exports.incrementSubmitCount = incrementSubmitCount;
2194
2628
  exports.isDirty = isDirty;
2195
2629
  exports.isTouched = isTouched;
2630
+ exports.registerFieldMode = registerFieldMode;
2196
2631
  exports.registerFieldValidateDeps = registerFieldValidateDeps;
2632
+ exports.registerValidatorByPath = registerValidatorByPath;
2197
2633
  exports.removeField = removeField;
2198
2634
  exports.removeFieldByPath = removeFieldByPath;
2199
2635
  exports.reset = reset;
@@ -2216,6 +2652,7 @@
2216
2652
  exports.setValueByPath = setValueByPath;
2217
2653
  exports.subscribe = subscribe;
2218
2654
  exports.trigger = trigger;
2655
+ exports.unregisterFieldMode = unregisterFieldMode;
2219
2656
  exports.unregisterFieldValidateDeps = unregisterFieldValidateDeps;
2220
2657
  exports.unsetValidatingByPath = unsetValidatingByPath;
2221
2658
  exports.useCanSubmit = useCanSubmit;
@@ -2230,9 +2667,16 @@
2230
2667
  exports.useFieldErrorsByPath = useFieldErrorsByPath;
2231
2668
  exports.useForm = useForm;
2232
2669
  exports.useFormContext = useFormContext;
2670
+ exports.useFormError = useFormError;
2671
+ exports.useFormErrors = useFormErrors;
2672
+ exports.useFormState = useFormState;
2233
2673
  exports.useHasErrors = useHasErrors;
2234
2674
  exports.useIsDirty = useIsDirty;
2675
+ exports.useIsLoading = useIsLoading;
2676
+ exports.useIsSubmitSuccessful = useIsSubmitSuccessful;
2235
2677
  exports.useIsSubmitting = useIsSubmitting;
2678
+ exports.useIsValid = useIsValid;
2679
+ exports.useIsValidating = useIsValidating;
2236
2680
  exports.useSubmitCount = useSubmitCount;
2237
2681
  exports.useTouched = useTouched;
2238
2682
  exports.useTouchedByPath = useTouchedByPath;
@@ -2240,6 +2684,9 @@
2240
2684
  exports.useValue = useValue;
2241
2685
  exports.useValueByPath = useValueByPath;
2242
2686
  exports.useWatch = useWatch;
2687
+ exports.useWatchCore = useWatchCore;
2688
+ exports.userBlur = userBlur;
2689
+ exports.userChangeByPath = userChangeByPath;
2243
2690
  exports.validate = validate;
2244
2691
 
2245
2692
  }));