@reformer/core 1.1.0-beta.8 → 2.0.0-beta.10

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 (151) hide show
  1. package/dist/behaviors/compute-from.d.ts +2 -0
  2. package/dist/behaviors/compute-from.js +31 -0
  3. package/dist/behaviors/copy-from.d.ts +2 -0
  4. package/dist/behaviors/copy-from.js +29 -0
  5. package/dist/behaviors/enable-when.d.ts +2 -0
  6. package/dist/behaviors/enable-when.js +25 -0
  7. package/dist/behaviors/reset-when.d.ts +2 -0
  8. package/dist/behaviors/reset-when.js +24 -0
  9. package/dist/behaviors/revalidate-when.d.ts +2 -0
  10. package/dist/behaviors/revalidate-when.js +18 -0
  11. package/dist/behaviors/sync-fields.d.ts +2 -0
  12. package/dist/behaviors/sync-fields.js +41 -0
  13. package/dist/behaviors/transform-value.d.ts +2 -0
  14. package/dist/behaviors/transform-value.js +45 -0
  15. package/dist/behaviors/watch-field.d.ts +2 -0
  16. package/dist/behaviors/watch-field.js +21 -0
  17. package/dist/behaviors.js +27 -20
  18. package/dist/core/behavior/behavior-context.d.ts +27 -13
  19. package/dist/core/behavior/behavior-registry.d.ts +15 -27
  20. package/dist/core/behavior/behaviors/compute-from.d.ts +50 -21
  21. package/dist/core/behavior/behaviors/copy-from.d.ts +39 -14
  22. package/dist/core/behavior/behaviors/enable-when.d.ts +88 -19
  23. package/dist/core/behavior/behaviors/reset-when.d.ts +31 -18
  24. package/dist/core/behavior/behaviors/revalidate-when.d.ts +40 -17
  25. package/dist/core/behavior/behaviors/sync-fields.d.ts +34 -14
  26. package/dist/core/behavior/behaviors/transform-value.d.ts +116 -44
  27. package/dist/core/behavior/behaviors/watch-field.d.ts +66 -21
  28. package/dist/core/behavior/compose-behavior.d.ts +2 -12
  29. package/dist/core/behavior/index.d.ts +0 -1
  30. package/dist/core/behavior/types.d.ts +2 -8
  31. package/dist/core/factories/node-factory.d.ts +6 -29
  32. package/dist/core/nodes/array-node.d.ts +42 -22
  33. package/dist/core/nodes/field-node.d.ts +51 -26
  34. package/dist/core/nodes/form-node.d.ts +18 -20
  35. package/dist/core/nodes/group-node.d.ts +37 -212
  36. package/dist/core/types/deep-schema.d.ts +2 -12
  37. package/dist/core/types/field-path.d.ts +1 -1
  38. package/dist/core/types/form-context.d.ts +27 -27
  39. package/dist/core/types/{group-node-proxy.d.ts → form-proxy.d.ts} +12 -42
  40. package/dist/core/types/index.d.ts +52 -6
  41. package/dist/core/types/validation-schema.d.ts +3 -12
  42. package/dist/core/utils/abstract-registry.d.ts +74 -0
  43. package/dist/core/utils/aggregate-signals.d.ts +71 -0
  44. package/dist/core/utils/create-form.d.ts +3 -20
  45. package/dist/core/utils/error-handler.d.ts +1 -18
  46. package/dist/core/utils/field-path-navigator.d.ts +1 -1
  47. package/dist/core/{validation → utils}/field-path.d.ts +23 -6
  48. package/dist/core/utils/form-observer.d.ts +176 -0
  49. package/dist/core/utils/form-proxy-builder.d.ts +25 -0
  50. package/dist/core/utils/form-submitter.d.ts +121 -0
  51. package/dist/core/utils/index.d.ts +10 -2
  52. package/dist/core/utils/registry-helpers.d.ts +0 -7
  53. package/dist/core/utils/safe-effect.d.ts +73 -0
  54. package/dist/core/utils/status-machine.d.ts +153 -0
  55. package/dist/core/utils/type-guards.d.ts +5 -23
  56. package/dist/core/utils/unique-id.d.ts +53 -0
  57. package/dist/core/validation/core/apply-when.d.ts +3 -9
  58. package/dist/core/validation/core/apply.d.ts +2 -13
  59. package/dist/core/validation/core/validate-async.d.ts +2 -8
  60. package/dist/core/validation/core/validate-tree.d.ts +0 -6
  61. package/dist/core/validation/core/validate.d.ts +1 -7
  62. package/dist/core/validation/index.d.ts +8 -2
  63. package/dist/core/validation/validate-form.d.ts +1 -38
  64. package/dist/core/validation/validation-applicator.d.ts +2 -21
  65. package/dist/core/validation/validation-context.d.ts +67 -28
  66. package/dist/core/validation/validation-registry.d.ts +11 -25
  67. package/dist/core/validation/validators/array-validators.d.ts +2 -12
  68. package/dist/core/validation/validators/date-utils.d.ts +26 -0
  69. package/dist/core/validation/validators/email.d.ts +2 -9
  70. package/dist/core/validation/validators/future-date.d.ts +35 -0
  71. package/dist/core/validation/validators/index.d.ts +7 -1
  72. package/dist/core/validation/validators/is-date.d.ts +36 -0
  73. package/dist/core/validation/validators/max-age.d.ts +36 -0
  74. package/dist/core/validation/validators/max-date.d.ts +36 -0
  75. package/dist/core/validation/validators/max-length.d.ts +3 -10
  76. package/dist/core/validation/validators/max.d.ts +3 -10
  77. package/dist/core/validation/validators/min-age.d.ts +36 -0
  78. package/dist/core/validation/validators/min-date.d.ts +36 -0
  79. package/dist/core/validation/validators/min-length.d.ts +3 -10
  80. package/dist/core/validation/validators/min.d.ts +3 -10
  81. package/dist/core/validation/validators/number.d.ts +2 -9
  82. package/dist/core/validation/validators/past-date.d.ts +35 -0
  83. package/dist/core/validation/validators/pattern.d.ts +2 -9
  84. package/dist/core/validation/validators/phone.d.ts +2 -9
  85. package/dist/core/validation/validators/required.d.ts +2 -9
  86. package/dist/core/validation/validators/url.d.ts +2 -9
  87. package/dist/date-utils-xUWFslTj.js +29 -0
  88. package/dist/field-path-DuKdGcIE.js +66 -0
  89. package/dist/hooks/types.d.ts +328 -0
  90. package/dist/hooks/useArrayLength.d.ts +31 -0
  91. package/dist/hooks/useFormControl.d.ts +15 -39
  92. package/dist/hooks/useFormControlValue.d.ts +167 -0
  93. package/dist/hooks/useHiddenCondition.d.ts +25 -0
  94. package/dist/hooks/useSignalSubscription.d.ts +17 -0
  95. package/dist/index-D25LsbRm.js +73 -0
  96. package/dist/index.d.ts +5 -1
  97. package/dist/index.js +1248 -1357
  98. package/dist/registry-helpers-Bv_BJ1s-.js +615 -0
  99. package/dist/safe-effect-Dh8uw81c.js +20 -0
  100. package/dist/validate-C3XiA_zf.js +10 -0
  101. package/dist/validators/email.d.ts +2 -0
  102. package/dist/validators/email.js +13 -0
  103. package/dist/validators/future-date.d.ts +2 -0
  104. package/dist/validators/future-date.js +20 -0
  105. package/dist/validators/is-date.d.ts +2 -0
  106. package/dist/validators/is-date.js +12 -0
  107. package/dist/validators/max-age.d.ts +2 -0
  108. package/dist/validators/max-age.js +20 -0
  109. package/dist/validators/max-date.d.ts +2 -0
  110. package/dist/validators/max-date.js +20 -0
  111. package/dist/validators/max-length.d.ts +2 -0
  112. package/dist/validators/max-length.js +11 -0
  113. package/dist/validators/max.d.ts +2 -0
  114. package/dist/validators/max.js +11 -0
  115. package/dist/validators/min-age.d.ts +2 -0
  116. package/dist/validators/min-age.js +20 -0
  117. package/dist/validators/min-date.d.ts +2 -0
  118. package/dist/validators/min-date.js +20 -0
  119. package/dist/validators/min-length.d.ts +2 -0
  120. package/dist/validators/min-length.js +11 -0
  121. package/dist/validators/min.d.ts +2 -0
  122. package/dist/validators/min.js +11 -0
  123. package/dist/validators/number.d.ts +2 -0
  124. package/dist/validators/number.js +35 -0
  125. package/dist/validators/past-date.d.ts +2 -0
  126. package/dist/validators/past-date.js +20 -0
  127. package/dist/validators/pattern.d.ts +2 -0
  128. package/dist/validators/pattern.js +11 -0
  129. package/dist/validators/phone.d.ts +2 -0
  130. package/dist/validators/phone.js +35 -0
  131. package/dist/validators/required.d.ts +2 -0
  132. package/dist/validators/required.js +15 -0
  133. package/dist/validators/url.d.ts +2 -0
  134. package/dist/validators/url.js +19 -0
  135. package/dist/validators-BGsNOgT1.js +207 -0
  136. package/dist/validators.js +53 -28
  137. package/llms.txt +8069 -357
  138. package/package.json +87 -8
  139. package/dist/behaviors-BRaiR-UY.js +0 -528
  140. package/dist/core/behavior/behavior-applicator.d.ts +0 -71
  141. package/dist/core/behavior/create-field-path.d.ts +0 -20
  142. package/dist/core/context/form-context-impl.d.ts +0 -29
  143. package/dist/core/nodes/group-node/field-registry.d.ts +0 -191
  144. package/dist/core/nodes/group-node/index.d.ts +0 -11
  145. package/dist/core/nodes/group-node/proxy-builder.d.ts +0 -71
  146. package/dist/core/nodes/group-node/state-manager.d.ts +0 -184
  147. package/dist/core/utils/debounce.d.ts +0 -160
  148. package/dist/core/utils/resources.d.ts +0 -41
  149. package/dist/core/validation/validators/date.d.ts +0 -38
  150. package/dist/registry-helpers-BfCZcMkO.js +0 -426
  151. package/dist/validators-DjXtDVoE.js +0 -455
package/dist/index.js CHANGED
@@ -1,12 +1,14 @@
1
- import { d, w as l, E as v, c as S } from "./behaviors-BRaiR-UY.js";
2
- import { i as oe } from "./behaviors-BRaiR-UY.js";
3
- import { i as R, V as P, T as M, c as x } from "./validators-DjXtDVoE.js";
4
- import { g as ue, e as he, b as de, d as ce, v as ve, a as fe } from "./validators-DjXtDVoE.js";
5
- import { V as C, B as T } from "./registry-helpers-BfCZcMkO.js";
6
- import { R as ye, a as me, g as ge } from "./registry-helpers-BfCZcMkO.js";
7
- import { v4 as D } from "uuid";
8
- import { useRef as V, useCallback as y, useSyncExternalStore as w } from "react";
9
- class A {
1
+ import { signal as g, computed as d, effect as m, batch as j } from "@preact/signals-core";
2
+ import { F as S, E as V, V as K, B as H } from "./registry-helpers-Bv_BJ1s-.js";
3
+ import { A as ke, R as xe, b as Fe, g as Te } from "./registry-helpers-Bv_BJ1s-.js";
4
+ import { i as M, a as N, A as z, V as J, T as Y } from "./validators-BGsNOgT1.js";
5
+ import { g as Pe, c as Ce, d as De, v as Re, b as Me } from "./validators-BGsNOgT1.js";
6
+ import { c as I } from "./field-path-DuKdGcIE.js";
7
+ import { a as Ie, e as Le, t as Be } from "./field-path-DuKdGcIE.js";
8
+ import U, { useRef as P, useCallback as b, useSyncExternalStore as Q } from "react";
9
+ import { i as $e } from "./index-D25LsbRm.js";
10
+ import { r as We, s as qe, a as je } from "./safe-effect-Dh8uw81c.js";
11
+ class C {
10
12
  // ============================================================================
11
13
  // Protected состояние (для Template Method паттерна)
12
14
  // ============================================================================
@@ -14,17 +16,17 @@ class A {
14
16
  * Пользователь взаимодействовал с узлом (touched)
15
17
  * Protected: наследники могут читать/изменять через методы
16
18
  */
17
- _touched = d(!1);
19
+ _touched = g(!1);
18
20
  /**
19
21
  * Значение узла было изменено (dirty)
20
22
  * Protected: наследники могут читать/изменять через методы
21
23
  */
22
- _dirty = d(!1);
24
+ _dirty = g(!1);
23
25
  /**
24
26
  * Текущий статус узла
25
27
  * Protected: наследники могут читать/изменять через методы
26
28
  */
27
- _status = d("valid");
29
+ _status = g("valid");
28
30
  // ============================================================================
29
31
  // Публичные computed signals (readonly для внешнего мира)
30
32
  // ============================================================================
@@ -32,33 +34,33 @@ class A {
32
34
  * Пользователь взаимодействовал с узлом (touched)
33
35
  * Computed из _touched для предоставления readonly интерфейса
34
36
  */
35
- touched = l(() => this._touched.value);
37
+ touched = d(() => this._touched.value);
36
38
  /**
37
39
  * Пользователь не взаимодействовал с узлом (untouched)
38
40
  */
39
- untouched = l(() => !this._touched.value);
41
+ untouched = d(() => !this._touched.value);
40
42
  /**
41
43
  * Значение узла было изменено (dirty)
42
44
  * Computed из _dirty для предоставления readonly интерфейса
43
45
  */
44
- dirty = l(() => this._dirty.value);
46
+ dirty = d(() => this._dirty.value);
45
47
  /**
46
48
  * Значение узла не было изменено (pristine)
47
49
  */
48
- pristine = l(() => !this._dirty.value);
50
+ pristine = d(() => !this._dirty.value);
49
51
  /**
50
52
  * Текущий статус узла
51
53
  * Computed из _status для предоставления readonly интерфейса
52
54
  */
53
- status = l(() => this._status.value);
55
+ status = d(() => this._status.value);
54
56
  /**
55
57
  * Узел отключен (disabled)
56
58
  */
57
- disabled = l(() => this._status.value === "disabled");
59
+ disabled = d(() => this._status.value === "disabled");
58
60
  /**
59
61
  * Узел включен (enabled)
60
62
  */
61
- enabled = l(() => this._status.value !== "disabled");
63
+ enabled = d(() => this._status.value !== "disabled");
62
64
  /**
63
65
  * Получить ошибки валидации с фильтрацией
64
66
  *
@@ -100,17 +102,17 @@ class A {
100
102
  */
101
103
  getErrors(e) {
102
104
  const t = this.errors.value;
103
- return e ? t.filter((i) => {
104
- if (e.code !== void 0 && !(Array.isArray(e.code) ? e.code : [e.code]).includes(i.code) || e.message !== void 0 && !i.message.toLowerCase().includes(e.message.toLowerCase()))
105
+ return e ? t.filter((s) => {
106
+ if (e.code !== void 0 && !(Array.isArray(e.code) ? e.code : [e.code]).includes(s.code) || e.message !== void 0 && !s.message.toLowerCase().includes(e.message.toLowerCase()))
105
107
  return !1;
106
108
  if (e.params !== void 0) {
107
- if (!i.params)
109
+ if (!s.params)
108
110
  return !1;
109
- for (const [s, a] of Object.entries(e.params))
110
- if (i.params[s] !== a)
111
+ for (const [i, r] of Object.entries(e.params))
112
+ if (s.params[i] !== r)
111
113
  return !1;
112
114
  }
113
- return !(e.predicate !== void 0 && !e.predicate(i));
115
+ return !(e.predicate !== void 0 && !e.predicate(s));
114
116
  }) : t;
115
117
  }
116
118
  // ============================================================================
@@ -283,7 +285,7 @@ class A {
283
285
  onEnable() {
284
286
  }
285
287
  }
286
- class _ {
288
+ class D {
287
289
  /**
288
290
  * Хранилище подписок
289
291
  * Ключ: уникальный идентификатор подписки
@@ -451,157 +453,196 @@ class _ {
451
453
  this.clear();
452
454
  }
453
455
  }
454
- var g = /* @__PURE__ */ ((r) => (r.THROW = "throw", r.LOG = "log", r.CONVERT = "convert", r))(g || {});
455
- class E {
456
+ const w = {
457
+ /** FieldNode.watch() */
458
+ Watch: "watch",
459
+ /** FieldNode.computeFrom() */
460
+ ComputeFrom: "computeFrom",
461
+ /** GroupNode.linkFields() */
462
+ LinkFields: "linkFields",
463
+ /** GroupNode.watchField() */
464
+ WatchField: "watchField",
465
+ /** ArrayNode.watchItems() */
466
+ WatchItems: "watchItems",
467
+ /** ArrayNode.watchLength() */
468
+ WatchLength: "watchLength"
469
+ };
470
+ let X = 0;
471
+ function A(a) {
472
+ return `${a}-${++X}`;
473
+ }
474
+ class Z {
475
+ /** Внутренний сигнал статуса */
476
+ _status;
477
+ /** Публичный read-only сигнал статуса */
478
+ status;
479
+ /** Поле валидно */
480
+ valid;
481
+ /** Поле невалидно */
482
+ invalid;
483
+ /** Идет валидация */
484
+ pending;
485
+ /** Поле отключено */
486
+ disabled;
456
487
  /**
457
- * Обработать ошибку согласно заданной стратегии
488
+ * @param initial - Начальный статус (по умолчанию 'valid')
489
+ */
490
+ constructor(e = "valid") {
491
+ this._status = g(e), this.status = d(() => this._status.value), this.valid = d(() => this._status.value === "valid"), this.invalid = d(() => this._status.value === "invalid"), this.pending = d(() => this._status.value === "pending"), this.disabled = d(() => this._status.value === "disabled");
492
+ }
493
+ /**
494
+ * Начать валидацию
458
495
  *
459
- * @param error Ошибка для обработки (Error | string | unknown)
460
- * @param context Контекст ошибки для логирования (например, 'AsyncValidator', 'BehaviorRegistry')
461
- * @param strategy Стратегия обработки (THROW | LOG | CONVERT)
462
- * @returns ValidationError если strategy = CONVERT, undefined если strategy = LOG, никогда не возвращается если strategy = THROW
496
+ * Переводит статус в 'pending' если поле не отключено
463
497
  *
464
498
  * @example
465
499
  * ```typescript
466
- * // THROW - пробросить ошибку
467
- * try {
468
- * riskyOperation();
469
- * } catch (error) {
470
- * FormErrorHandler.handle(error, 'RiskyOperation', ErrorStrategy.THROW);
471
- * // Этот код никогда не выполнится
472
- * }
500
+ * statusMachine.startValidation();
501
+ * // status: 'pending'
502
+ * ```
503
+ */
504
+ startValidation() {
505
+ this._status.value !== "disabled" && (this._status.value = "pending");
506
+ }
507
+ /**
508
+ * Завершить валидацию
473
509
  *
474
- * // LOG - залогировать и продолжить
475
- * try {
476
- * nonCriticalOperation();
477
- * } catch (error) {
478
- * FormErrorHandler.handle(error, 'NonCritical', ErrorStrategy.LOG);
479
- * // Продолжаем выполнение
480
- * }
510
+ * @param hasErrors - Есть ли ошибки валидации
481
511
  *
482
- * // CONVERT - конвертировать в ValidationError
483
- * try {
484
- * await validator(value);
485
- * } catch (error) {
486
- * const validationError = FormErrorHandler.handle(
487
- * error,
488
- * 'AsyncValidator',
489
- * ErrorStrategy.CONVERT
490
- * );
491
- * return validationError;
492
- * }
512
+ * @example
513
+ * ```typescript
514
+ * // Валидация успешна
515
+ * statusMachine.completeValidation(false);
516
+ * // status: 'valid'
517
+ *
518
+ * // Есть ошибки
519
+ * statusMachine.completeValidation(true);
520
+ * // status: 'invalid'
493
521
  * ```
494
522
  */
495
- static handle(e, t, i = "throw") {
496
- const s = this.extractMessage(e);
497
- switch (i) {
498
- case "throw":
499
- throw e;
500
- case "log":
501
- return;
502
- case "convert":
503
- return {
504
- code: "validator_error",
505
- message: s,
506
- params: { field: t }
507
- };
508
- }
523
+ completeValidation(e) {
524
+ (this._status.value === "pending" || this._status.value !== "disabled") && (this._status.value = e ? "invalid" : "valid");
509
525
  }
510
526
  /**
511
- * Извлечь сообщение из ошибки
527
+ * Установить ошибки напрямую (без перехода через pending)
512
528
  *
513
- * Обрабатывает различные типы ошибок:
514
- * - Error объекты → error.message
515
- * - Строки → возвращает как есть
516
- * - Объекты с message → извлекает message
517
- * - Другое → String(error)
529
+ * Используется для синхронной валидации или установки ошибок извне
518
530
  *
519
- * @param error Ошибка для извлечения сообщения
520
- * @returns Сообщение ошибки
521
- * @private
531
+ * @param hasErrors - Есть ли ошибки
522
532
  *
523
533
  * @example
524
534
  * ```typescript
525
- * FormErrorHandler.extractMessage(new Error('Test'));
526
- * // 'Test'
527
- *
528
- * FormErrorHandler.extractMessage('String error');
529
- * // 'String error'
535
+ * statusMachine.setErrors(true);
536
+ * // status: 'invalid'
537
+ * ```
538
+ */
539
+ setErrors(e) {
540
+ this._status.value !== "disabled" && (this._status.value = e ? "invalid" : "valid");
541
+ }
542
+ /**
543
+ * Отключить поле
530
544
  *
531
- * FormErrorHandler.extractMessage({ message: 'Object error' });
532
- * // 'Object error'
545
+ * Переводит статус в 'disabled'
533
546
  *
534
- * FormErrorHandler.extractMessage(null);
535
- * // 'null'
547
+ * @example
548
+ * ```typescript
549
+ * statusMachine.disable();
550
+ * // status: 'disabled'
536
551
  * ```
537
552
  */
538
- static extractMessage(e) {
539
- return e instanceof Error ? e.message : typeof e == "string" ? e : typeof e == "object" && e !== null && "message" in e ? String(e.message) : String(e);
553
+ disable() {
554
+ this._status.value = "disabled";
540
555
  }
541
556
  /**
542
- * Создать ValidationError с заданными параметрами
543
- *
544
- * Утилитная функция для создания ValidationError объектов
557
+ * Включить поле
545
558
  *
546
- * @param code Код ошибки
547
- * @param message Сообщение ошибки
548
- * @param field Поле (опционально)
549
- * @returns ValidationError объект
559
+ * @param hasErrors - Есть ли ошибки (определяет valid/invalid)
550
560
  *
551
561
  * @example
552
562
  * ```typescript
553
- * const error = FormErrorHandler.createValidationError(
554
- * 'required',
555
- * 'This field is required',
556
- * 'email'
557
- * );
558
- * // { code: 'required', message: 'This field is required', field: 'email' }
563
+ * statusMachine.enable(false);
564
+ * // status: 'valid'
565
+ *
566
+ * statusMachine.enable(true);
567
+ * // status: 'invalid'
559
568
  * ```
560
569
  */
561
- static createValidationError(e, t, i) {
562
- return {
563
- code: e,
564
- message: t,
565
- params: i ? { field: i } : void 0
566
- };
570
+ enable(e = !1) {
571
+ this._status.value = e ? "invalid" : "valid";
567
572
  }
568
573
  /**
569
- * Проверить, является ли объект ValidationError
574
+ * Обработать событие (альтернативный API)
570
575
  *
571
- * Type guard для ValidationError
572
- *
573
- * @param value Значение для проверки
574
- * @returns true если value является ValidationError
576
+ * @param event - Событие для обработки
575
577
  *
576
578
  * @example
577
579
  * ```typescript
578
- * if (FormErrorHandler.isValidationError(result)) {
579
- * console.log(result.code); // OK, типобезопасно
580
- * }
580
+ * statusMachine.dispatch({ type: 'START_VALIDATION' });
581
+ * statusMachine.dispatch({ type: 'VALIDATION_FAILURE' });
581
582
  * ```
582
583
  */
583
- static isValidationError(e) {
584
- return typeof e == "object" && e !== null && "code" in e && "message" in e && typeof e.code == "string" && typeof e.message == "string";
584
+ dispatch(e) {
585
+ switch (e.type) {
586
+ case "START_VALIDATION":
587
+ this.startValidation();
588
+ break;
589
+ case "VALIDATION_SUCCESS":
590
+ this.completeValidation(!1);
591
+ break;
592
+ case "VALIDATION_FAILURE":
593
+ this.completeValidation(!0);
594
+ break;
595
+ case "DISABLE":
596
+ this.disable();
597
+ break;
598
+ case "ENABLE":
599
+ this.enable(e.hasErrors);
600
+ break;
601
+ case "SET_ERRORS":
602
+ this.setErrors(e.hasErrors);
603
+ break;
604
+ }
605
+ }
606
+ /**
607
+ * Получить текущий статус
608
+ */
609
+ getStatus() {
610
+ return this._status.value;
611
+ }
612
+ /**
613
+ * Проверить, можно ли начать валидацию
614
+ */
615
+ canValidate() {
616
+ return this._status.value !== "disabled";
585
617
  }
586
618
  }
587
- class I extends A {
619
+ class ee extends C {
588
620
  // ============================================================================
589
621
  // Приватные сигналы
590
622
  // ============================================================================
591
623
  _value;
592
624
  _errors;
593
- // _touched, _dirty, _status наследуются от FormNode (protected)
594
- _pending;
625
+ // _touched, _dirty наследуются от FormNode (protected)
626
+ // _status управляется через statusMachine
595
627
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
596
628
  _componentProps;
629
+ /**
630
+ * State machine для управления статусом поля
631
+ * Централизует логику переходов между valid/invalid/pending/disabled
632
+ */
633
+ statusMachine;
597
634
  // ============================================================================
598
635
  // Публичные computed signals
599
636
  // ============================================================================
600
637
  value;
638
+ // valid, invalid, pending, status, disabled берутся из statusMachine
601
639
  valid;
602
640
  invalid;
603
- // touched, dirty, status наследуются от FormNode
604
641
  pending;
642
+ // Override status и disabled из базового класса
643
+ status;
644
+ disabled;
645
+ // touched, dirty наследуются от FormNode
605
646
  errors;
606
647
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
607
648
  componentProps;
@@ -617,22 +658,26 @@ class I extends A {
617
658
  asyncValidators;
618
659
  updateOn;
619
660
  initialValue;
620
- currentValidationId = 0;
661
+ currentAbortController;
621
662
  debounceMs;
622
663
  validateDebounceTimer;
623
- validateDebounceResolve;
664
+ /**
665
+ * Pending debounced validation state
666
+ * Contains resolve function and AbortController for cancellation
667
+ */
668
+ pendingValidation;
624
669
  /**
625
670
  * Менеджер подписок для централизованного cleanup
626
671
  * Использует SubscriptionManager вместо массива для управления подписками
627
672
  */
628
- disposers = new _();
673
+ disposers = new D();
629
674
  component;
630
675
  // ============================================================================
631
676
  // Конструктор
632
677
  // ============================================================================
633
678
  constructor(e) {
634
- super(), this.initialValue = e.value, this.validators = e.validators || [], this.asyncValidators = e.asyncValidators || [], this.updateOn = e.updateOn || "blur", this.debounceMs = e.debounce || 0, this.component = e.component, this._value = d(e.value), this._errors = d([]), this._pending = d(!1), this._componentProps = d(e.componentProps || {}), e.disabled && (this._status.value = "disabled"), this.value = l(() => this._value.value), this.valid = l(() => this._status.value === "valid"), this.invalid = l(() => this._status.value === "invalid"), this.pending = l(() => this._pending.value), this.errors = l(() => this._errors.value), this.componentProps = l(() => this._componentProps.value), this.shouldShowError = l(
635
- () => this._status.value === "invalid" && (this._touched.value || this._dirty.value)
679
+ super(), this.initialValue = e.value, this.validators = e.validators || [], this.asyncValidators = e.asyncValidators || [], this.updateOn = e.updateOn || "blur", this.debounceMs = e.debounce || 0, this.component = e.component, this._value = g(e.value), this._errors = g([]), this._componentProps = g(e.componentProps || {}), this.statusMachine = new Z(e.disabled ? "disabled" : "valid"), this.value = d(() => this._value.value), this.valid = d(() => this.statusMachine.valid.value), this.invalid = d(() => this.statusMachine.invalid.value), this.pending = d(() => this.statusMachine.pending.value), this.status = d(() => this.statusMachine.status.value), this.disabled = d(() => this.statusMachine.disabled.value), this.errors = d(() => this._errors.value), this.componentProps = d(() => this._componentProps.value), this.shouldShowError = d(
680
+ () => this.statusMachine.invalid.value && (this._touched.value || this._dirty.value)
636
681
  );
637
682
  }
638
683
  // ============================================================================
@@ -644,12 +689,12 @@ class I extends A {
644
689
  setValue(e, t) {
645
690
  if (this._value.value = e, this._dirty.value = !0, t?.emitEvent === !1)
646
691
  return;
647
- const i = this.validators.length > 0 || this.asyncValidators.length > 0, s = this._errors.value.length > 0;
692
+ const s = this.validators.length > 0 || this.asyncValidators.length > 0, i = this._errors.value.length > 0;
648
693
  if (this.updateOn === "change") {
649
694
  this.validate();
650
695
  return;
651
696
  }
652
- s && i && this.validate();
697
+ i && s && this.validate();
653
698
  }
654
699
  patchValue(e) {
655
700
  this.setValue(e);
@@ -678,7 +723,7 @@ class I extends A {
678
723
  * ```
679
724
  */
680
725
  reset(e) {
681
- this._value.value = e !== void 0 ? e : this.initialValue, this._errors.value = [], this._touched.value = !1, this._dirty.value = !1, this._status.value = "valid";
726
+ this._value.value = e !== void 0 ? e : this.initialValue, this._errors.value = [], this._touched.value = !1, this._dirty.value = !1, this.statusMachine.setErrors(!1);
682
727
  }
683
728
  /**
684
729
  * Сбросить поле к исходному значению (initialValue)
@@ -708,13 +753,25 @@ class I extends A {
708
753
  resetToInitial() {
709
754
  this.reset(this.initialValue);
710
755
  }
756
+ /**
757
+ * Cancel any pending validation (debounced or running)
758
+ * @private
759
+ * @remarks
760
+ * Centralizes all cancellation logic:
761
+ * - Aborts pending debounced validation and resolves its promise
762
+ * - Clears debounce timer
763
+ * - Aborts currently running async validation
764
+ */
765
+ cancelPendingValidation() {
766
+ this.pendingValidation && (this.pendingValidation.abortController.abort(), this.pendingValidation.resolve(!1), this.pendingValidation = void 0), this.validateDebounceTimer && (clearTimeout(this.validateDebounceTimer), this.validateDebounceTimer = void 0), this.currentAbortController && (this.currentAbortController.abort(), this.currentAbortController = void 0);
767
+ }
711
768
  /**
712
769
  * Запустить валидацию поля
713
770
  * @param options - опции валидации
714
771
  * @returns `Promise<boolean>` - true если поле валидно
715
772
  *
716
773
  * @remarks
717
- * Метод защищен от race conditions через validationId.
774
+ * Метод защищен от race conditions через AbortController.
718
775
  * При быстром вводе только последняя валидация применяет результаты.
719
776
  *
720
777
  * @example
@@ -728,69 +785,102 @@ class I extends A {
728
785
  */
729
786
  async validate(e) {
730
787
  const t = e?.debounce ?? this.debounceMs;
731
- return t > 0 && this.asyncValidators.length > 0 ? new Promise((i) => {
732
- const s = this.currentValidationId;
733
- this.validateDebounceResolve && this.validateDebounceResolve(!1), this.validateDebounceTimer && clearTimeout(this.validateDebounceTimer), this.validateDebounceResolve = i, this.validateDebounceTimer = setTimeout(async () => {
734
- if (this.validateDebounceResolve = void 0, s !== this.currentValidationId) {
788
+ if (this.cancelPendingValidation(), t <= 0 || this.asyncValidators.length === 0)
789
+ return this.validateImmediate();
790
+ const s = new AbortController();
791
+ return new Promise((i) => {
792
+ this.pendingValidation = { resolve: i, abortController: s }, this.validateDebounceTimer = setTimeout(async () => {
793
+ if (this.validateDebounceTimer = void 0, s.signal.aborted) {
735
794
  i(!1);
736
795
  return;
737
796
  }
738
- const a = await this.validateImmediate();
739
- i(a);
740
- }, t);
741
- }) : this.validateImmediate();
797
+ this.pendingValidation = void 0;
798
+ const r = await this.validateImmediate(s);
799
+ i(r);
800
+ }, t), s.signal.addEventListener(
801
+ "abort",
802
+ () => {
803
+ this.validateDebounceTimer && (clearTimeout(this.validateDebounceTimer), this.validateDebounceTimer = void 0), i(!1);
804
+ },
805
+ { once: !0 }
806
+ );
807
+ });
742
808
  }
743
809
  /**
744
810
  * Немедленная валидация без debounce
745
811
  * @private
812
+ * @param providedController - AbortController from debounced validate()
746
813
  * @remarks
747
- * Защищена от race conditions:
748
- * - Проверка validationId после синхронной валидации
749
- * - Проверка перед установкой pending
750
- * - Проверка после Promise.all
751
- * - Проверка перед обработкой async результатов
752
- * - Проверка перед очисткой errors
753
- */
754
- async validateImmediate() {
755
- const e = ++this.currentValidationId, t = [];
756
- for (const s of this.validators) {
757
- const a = s(this._value.value);
758
- a && t.push(a);
814
+ * Защищена от race conditions через AbortController:
815
+ * - Отменяет предыдущую валидацию при запуске новой (if no controller provided)
816
+ * - Передаёт AbortSignal в async валидаторы для отмены операций (например, fetch)
817
+ * - Проверяет signal.aborted в ключевых точках
818
+ */
819
+ async validateImmediate(e) {
820
+ const t = e ?? new AbortController();
821
+ e || this.currentAbortController?.abort(), this.currentAbortController = t;
822
+ const { signal: s } = t, i = [];
823
+ for (const n of this.validators) {
824
+ const o = n(this._value.value);
825
+ o && i.push(o);
759
826
  }
760
- if (e !== this.currentValidationId)
827
+ if (s.aborted)
761
828
  return !1;
762
- if (t.length > 0)
763
- return this._errors.value = t, this._status.value = "invalid", !1;
764
- if (this.asyncValidators.length > 0) {
765
- if (e !== this.currentValidationId)
829
+ if (i.length > 0) {
830
+ this._errors.value = i;
831
+ const n = i.some((o) => o.severity !== "warning");
832
+ if (this.statusMachine.setErrors(n), n)
766
833
  return !1;
767
- this._pending.value = !0, this._status.value = "pending";
768
- const s = await Promise.all(
769
- this.asyncValidators.map(async (n) => {
770
- try {
771
- return await n(this._value.value);
772
- } catch (o) {
773
- return E.handle(
774
- o,
775
- "FieldNode AsyncValidator",
776
- g.CONVERT
777
- );
778
- }
779
- })
780
- );
781
- if (e !== this.currentValidationId || (this._pending.value = !1, e !== this.currentValidationId))
834
+ }
835
+ if (this.asyncValidators.length > 0) {
836
+ if (s.aborted)
782
837
  return !1;
783
- const a = s.filter(Boolean);
784
- if (a.length > 0)
785
- return this._errors.value = a, this._status.value = "invalid", !1;
838
+ this.statusMachine.startValidation();
839
+ try {
840
+ const n = await Promise.all(
841
+ this.asyncValidators.map(async (l) => {
842
+ if (s.aborted)
843
+ throw new DOMException("Validation aborted", "AbortError");
844
+ try {
845
+ const u = await l(this._value.value, { signal: s });
846
+ if (s.aborted)
847
+ throw new DOMException("Validation aborted", "AbortError");
848
+ return u;
849
+ } catch (u) {
850
+ if (u instanceof DOMException && u.name === "AbortError")
851
+ throw u;
852
+ return S.handle(
853
+ u,
854
+ "FieldNode AsyncValidator",
855
+ V.CONVERT
856
+ );
857
+ }
858
+ })
859
+ );
860
+ if (s.aborted)
861
+ return !1;
862
+ const o = n.filter(Boolean);
863
+ if (o.length > 0) {
864
+ this._errors.value = o;
865
+ const l = o.some((u) => u.severity !== "warning");
866
+ if (this.statusMachine.completeValidation(l), l)
867
+ return !1;
868
+ }
869
+ } catch (n) {
870
+ if (n instanceof DOMException && n.name === "AbortError")
871
+ return !1;
872
+ throw n;
873
+ }
786
874
  }
787
- return e !== this.currentValidationId ? !1 : ((this.validators.length > 0 || this.asyncValidators.length > 0) && (this._errors.value = [], this._status.value = "valid"), this._errors.value.length === 0);
875
+ return s.aborted ? !1 : ((this.validators.length > 0 || this.asyncValidators.length > 0) && (this._errors.value = [], this.statusMachine.completeValidation(!1)), !this._errors.value.some((n) => n.severity !== "warning"));
788
876
  }
789
877
  setErrors(e) {
790
- this._errors.value = e, this._status.value = e.length > 0 ? "invalid" : "valid";
878
+ this._errors.value = e;
879
+ const t = e.some((s) => s.severity !== "warning");
880
+ this.statusMachine.setErrors(t);
791
881
  }
792
882
  clearErrors() {
793
- this._errors.value = [], this._status.value = "valid";
883
+ this._errors.value = [], this.statusMachine.setErrors(!1);
794
884
  }
795
885
  // ============================================================================
796
886
  // Protected hooks (Template Method pattern)
@@ -806,18 +896,18 @@ class I extends A {
806
896
  /**
807
897
  * Hook: вызывается после disable()
808
898
  *
809
- * Для FieldNode: очищаем ошибки валидации
899
+ * Для FieldNode: синхронизируем statusMachine и очищаем ошибки
810
900
  */
811
901
  onDisable() {
812
- this._errors.value = [];
902
+ this.statusMachine.disable(), this._errors.value = [];
813
903
  }
814
904
  /**
815
905
  * Hook: вызывается после enable()
816
906
  *
817
- * Для FieldNode: запускаем валидацию
907
+ * Для FieldNode: синхронизируем statusMachine и запускаем валидацию
818
908
  */
819
909
  onEnable() {
820
- this.validate();
910
+ this.statusMachine.enable(this._errors.value.length > 0), this.validate();
821
911
  }
822
912
  /**
823
913
  * Обновляет свойства компонента (например, опции для Select)
@@ -882,25 +972,35 @@ class I extends A {
882
972
  * Подписка на изменения значения поля
883
973
  * Автоматически отслеживает изменения через @preact/signals effect
884
974
  *
885
- * @param callback - Функция, вызываемая при изменении значения
975
+ * @param callback - Функция, вызываемая при изменении значения.
976
+ * Для async операций передается AbortSignal во втором параметре.
886
977
  * @returns Функция отписки для cleanup
887
978
  *
888
979
  * @example
889
980
  * ```typescript
981
+ * // Синхронный callback
890
982
  * const unsubscribe = form.email.watch((value) => {
891
983
  * console.log('Email changed:', value);
892
984
  * });
893
985
  *
986
+ * // Асинхронный callback с поддержкой отмены
987
+ * const unsubscribe = form.email.watch(async (value, signal) => {
988
+ * const result = await fetch('/api/validate', { signal });
989
+ * // ...
990
+ * });
991
+ *
894
992
  * // Cleanup
895
993
  * useEffect(() => unsubscribe, []);
896
994
  * ```
897
995
  */
898
996
  watch(e) {
899
- const t = v(() => {
900
- const s = this.value.value;
901
- e(s);
902
- }), i = `watch-${Date.now()}-${Math.random()}`;
903
- return this.disposers.add(i, t);
997
+ const t = new AbortController(), s = m(() => {
998
+ const r = this.value.value;
999
+ e(r, t.signal);
1000
+ }), i = A(w.Watch);
1001
+ return this.disposers.add(i, () => {
1002
+ t.abort(), s();
1003
+ });
904
1004
  }
905
1005
  /**
906
1006
  * Вычисляемое значение из других полей
@@ -925,16 +1025,23 @@ class I extends A {
925
1025
  * ```
926
1026
  */
927
1027
  computeFrom(e, t) {
928
- const i = v(() => {
929
- const a = e.map((o) => o.value), n = t(...a);
1028
+ const s = m(() => {
1029
+ const r = e.map((o) => o.value), n = t(...r);
930
1030
  this.setValue(n, { emitEvent: !1 });
931
- }), s = `computeFrom-${Date.now()}-${Math.random()}`;
932
- return this.disposers.add(s, i);
1031
+ }), i = A(w.ComputeFrom);
1032
+ return this.disposers.add(i, s);
933
1033
  }
934
1034
  /**
935
1035
  * Очистить все ресурсы и таймеры
936
1036
  * Должен вызываться при unmount компонента
937
1037
  *
1038
+ * @remarks
1039
+ * Освобождает все ресурсы:
1040
+ * - Отписывает все subscriptions через SubscriptionManager
1041
+ * - Отменяет pending/running валидации через cancelPendingValidation()
1042
+ *
1043
+ * Использует try-finally для гарантированного cleanup даже при ошибках.
1044
+ *
938
1045
  * @example
939
1046
  * ```typescript
940
1047
  * useEffect(() => {
@@ -945,10 +1052,14 @@ class I extends A {
945
1052
  * ```
946
1053
  */
947
1054
  dispose() {
948
- this.disposers.dispose(), this.validateDebounceTimer && (clearTimeout(this.validateDebounceTimer), this.validateDebounceTimer = void 0);
1055
+ try {
1056
+ this.disposers.dispose();
1057
+ } finally {
1058
+ this.cancelPendingValidation();
1059
+ }
949
1060
  }
950
1061
  }
951
- class N {
1062
+ class te {
952
1063
  form;
953
1064
  constructor(e) {
954
1065
  this.form = e;
@@ -964,8 +1075,8 @@ class N {
964
1075
  * @param validators Зарегистрированные валидаторы
965
1076
  */
966
1077
  async apply(e) {
967
- const { validatorsByField: t, treeValidators: i } = this.groupValidators(e);
968
- await this.applyFieldValidators(t), this.applyTreeValidators(i);
1078
+ const { validatorsByField: t, treeValidators: s } = this.groupValidators(e);
1079
+ await this.applyFieldValidators(t), this.applyTreeValidators(s);
969
1080
  }
970
1081
  /**
971
1082
  * Группировка валидаторов по типам
@@ -978,15 +1089,15 @@ class N {
978
1089
  * @returns Сгруппированные валидаторы
979
1090
  */
980
1091
  groupValidators(e) {
981
- const t = /* @__PURE__ */ new Map(), i = [];
982
- for (const s of e)
983
- if (s.type === "tree")
984
- i.push(s);
1092
+ const t = /* @__PURE__ */ new Map(), s = [];
1093
+ for (const i of e)
1094
+ if (i.type === "tree")
1095
+ s.push(i);
985
1096
  else {
986
- const a = t.get(s.fieldPath) || [];
987
- a.push(s), t.set(s.fieldPath, a);
1097
+ const r = t.get(i.fieldPath) || [];
1098
+ r.push(i), t.set(i.fieldPath, r);
988
1099
  }
989
- return { validatorsByField: t, treeValidators: i };
1100
+ return { validatorsByField: t, treeValidators: s };
990
1101
  }
991
1102
  /**
992
1103
  * Применение field валидаторов к полям
@@ -1000,31 +1111,44 @@ class N {
1000
1111
  * @param validatorsByField Валидаторы, сгруппированные по полям
1001
1112
  */
1002
1113
  async applyFieldValidators(e) {
1003
- for (const [t, i] of e) {
1004
- const s = this.form.getFieldByPath(t);
1005
- if (!s) {
1114
+ for (const [t, s] of e) {
1115
+ const i = this.form.getFieldByPath(t);
1116
+ if (!i) {
1006
1117
  console.warn(`Field ${t} not found in GroupNode`);
1007
1118
  continue;
1008
1119
  }
1009
- if (!R(s)) {
1010
- process.env.NODE_ENV !== "production" && console.warn(`Validation can only run on FieldNode, skipping ${t}`);
1120
+ if (!M(i) && !N(i)) {
1121
+ process.env.NODE_ENV !== "production" && console.warn(`Validation can only run on FieldNode or ArrayNode, skipping ${t}`);
1011
1122
  continue;
1012
1123
  }
1013
- const a = [], n = new P(this.form, t, s);
1014
- for (const o of i)
1124
+ const r = [];
1125
+ let n;
1126
+ if (N(i)) {
1127
+ const o = i.getValue();
1128
+ n = new z(this.form, t, o);
1129
+ } else
1130
+ n = new J(this.form, t, i);
1131
+ for (const o of s)
1015
1132
  if (!(o.condition && !this.checkCondition(o.condition)))
1016
1133
  try {
1017
- let u = null;
1018
- const h = n.value(), c = o.validator;
1019
- o.type === "sync" ? u = c(h, n) : o.type === "async" && (u = await c(h, n)), u && a.push(u);
1020
- } catch (u) {
1021
- E.handle(
1022
- u,
1134
+ let l = null;
1135
+ const u = n.value();
1136
+ if (o.type === "sync") {
1137
+ const v = o.validator;
1138
+ l = v(u, n);
1139
+ } else if (o.type === "async") {
1140
+ const v = o.validator;
1141
+ l = await v(u, n);
1142
+ }
1143
+ l && r.push(l);
1144
+ } catch (l) {
1145
+ S.handle(
1146
+ l,
1023
1147
  `ValidationApplicator: validator for ${t}`,
1024
- g.LOG
1148
+ V.LOG
1025
1149
  );
1026
1150
  }
1027
- a.length > 0 ? s.setErrors(a) : s.errors.value.length > 0 && !s.errors.value.some((o) => o.code !== "contextual") && s.clearErrors();
1151
+ r.length > 0 ? i.setErrors(r) : i.clearErrors();
1028
1152
  }
1029
1153
  }
1030
1154
  /**
@@ -1037,24 +1161,24 @@ class N {
1037
1161
  */
1038
1162
  applyTreeValidators(e) {
1039
1163
  for (const t of e) {
1040
- const i = new M(this.form);
1164
+ const s = new Y(this.form);
1041
1165
  if (!(t.condition && !this.checkCondition(t.condition)))
1042
1166
  try {
1043
1167
  if (t.type !== "tree")
1044
1168
  continue;
1045
- const s = t.validator(i);
1046
- if (s && t.options && "targetField" in t.options) {
1047
- const a = t.options.targetField;
1048
- if (a) {
1049
- const n = this.form.getFieldByPath(String(a));
1050
- if (n && R(n)) {
1051
- const o = n.errors.value;
1052
- n.setErrors([...o, s]);
1169
+ const i = t.validator, r = i(s);
1170
+ if (r && t.options && "targetField" in t.options) {
1171
+ const n = t.options.targetField;
1172
+ if (n) {
1173
+ const o = this.form.getFieldByPath(String(n));
1174
+ if (o && M(o)) {
1175
+ const l = o.errors.value;
1176
+ o.setErrors([...l, r]);
1053
1177
  }
1054
1178
  }
1055
1179
  }
1056
- } catch (s) {
1057
- E.handle(s, "ValidationApplicator: tree validator", g.LOG);
1180
+ } catch (i) {
1181
+ S.handle(i, "ValidationApplicator: tree validator", V.LOG);
1058
1182
  }
1059
1183
  }
1060
1184
  }
@@ -1071,61 +1195,11 @@ class N {
1071
1195
  const t = this.form.getFieldByPath(e.fieldPath);
1072
1196
  if (!t)
1073
1197
  return !1;
1074
- const i = t.value.value;
1075
- return e.conditionFn(i);
1076
- }
1077
- }
1078
- class B {
1079
- form;
1080
- behaviorRegistry;
1081
- constructor(e, t) {
1082
- this.form = e, this.behaviorRegistry = t;
1083
- }
1084
- /**
1085
- * Применить behavior схему к форме
1086
- *
1087
- * Этапы:
1088
- * 1. Начать регистрацию (beginRegistration)
1089
- * 2. Выполнить схему (регистрация behaviors)
1090
- * 3. Завершить регистрацию (endRegistration) - применить behaviors
1091
- * 4. Вернуть функцию cleanup для отписки
1092
- *
1093
- * @param schemaFn Функция-схема behavior
1094
- * @returns Функция отписки от всех behaviors
1095
- *
1096
- * @example
1097
- * ```typescript
1098
- * const cleanup = behaviorApplicator.apply((path) => {
1099
- * copyFrom(path.residenceAddress, path.registrationAddress, {
1100
- * when: (form) => form.sameAsRegistration === true
1101
- * });
1102
- *
1103
- * enableWhen(path.propertyValue, (form) => form.loanType === 'mortgage');
1104
- *
1105
- * computeFrom(
1106
- * path.initialPayment,
1107
- * [path.propertyValue],
1108
- * (propertyValue) => propertyValue ? propertyValue * 0.2 : null
1109
- * );
1110
- * });
1111
- *
1112
- * // Cleanup при unmount
1113
- * useEffect(() => cleanup, []);
1114
- * ```
1115
- */
1116
- apply(e) {
1117
- this.behaviorRegistry.beginRegistration();
1118
- try {
1119
- const t = S();
1120
- e(t);
1121
- const i = this.form.getProxy();
1122
- return this.behaviorRegistry.endRegistration(i).cleanup;
1123
- } catch (t) {
1124
- throw E.handle(t, "BehaviorApplicator", g.THROW), t;
1125
- }
1198
+ const s = t.value.value;
1199
+ return e.conditionFn(s);
1126
1200
  }
1127
1201
  }
1128
- class O {
1202
+ class se {
1129
1203
  /**
1130
1204
  * Парсит путь в массив сегментов
1131
1205
  *
@@ -1152,22 +1226,22 @@ class O {
1152
1226
  */
1153
1227
  parsePath(e) {
1154
1228
  const t = [];
1155
- let i = "", s = !1;
1156
- for (let a = 0; a < e.length; a++) {
1157
- const n = e[a];
1158
- n === "[" ? (s = !0, i += n) : n === "]" ? (s = !1, i += n) : n === "." && !s ? i && (this.addSegment(t, i), i = "") : i += n;
1229
+ let s = "", i = !1;
1230
+ for (let r = 0; r < e.length; r++) {
1231
+ const n = e[r];
1232
+ n === "[" ? (i = !0, s += n) : n === "]" ? (i = !1, s += n) : n === "." && !i ? s && (this.addSegment(t, s), s = "") : s += n;
1159
1233
  }
1160
- return i && this.addSegment(t, i), t;
1234
+ return s && this.addSegment(t, s), t;
1161
1235
  }
1162
1236
  /**
1163
1237
  * Добавляет сегмент в массив, обрабатывая массивы
1164
1238
  * @private
1165
1239
  */
1166
1240
  addSegment(e, t) {
1167
- const i = t.match(/^(.+)\[(\d+)\]$/);
1168
- i ? e.push({
1169
- key: i[1],
1170
- index: parseInt(i[2], 10)
1241
+ const s = t.match(/^(.+)\[(\d+)\]$/);
1242
+ s ? e.push({
1243
+ key: s[1],
1244
+ index: parseInt(s[2], 10)
1171
1245
  }) : e.push({ key: t });
1172
1246
  }
1173
1247
  /**
@@ -1202,16 +1276,16 @@ class O {
1202
1276
  * ```
1203
1277
  */
1204
1278
  getValueByPath(e, t) {
1205
- const i = this.parsePath(t);
1206
- let s = e;
1207
- for (const a of i) {
1208
- if (s == null) return;
1209
- if (s = s[a.key], a.index !== void 0) {
1210
- if (!Array.isArray(s)) return;
1211
- s = s[a.index];
1279
+ const s = this.parsePath(t);
1280
+ let i = e;
1281
+ for (const r of s) {
1282
+ if (i == null) return;
1283
+ if (i = i[r.key], r.index !== void 0) {
1284
+ if (!Array.isArray(i)) return;
1285
+ i = i[r.index];
1212
1286
  }
1213
1287
  }
1214
- return s;
1288
+ return i;
1215
1289
  }
1216
1290
  /**
1217
1291
  * Устанавливает значение по пути в объекте (мутирует объект)
@@ -1240,31 +1314,31 @@ class O {
1240
1314
  * // obj3.items[0].title === 'New'
1241
1315
  * ```
1242
1316
  */
1243
- setValueByPath(e, t, i) {
1244
- const s = this.parsePath(t);
1245
- if (s.length === 0)
1317
+ setValueByPath(e, t, s) {
1318
+ const i = this.parsePath(t);
1319
+ if (i.length === 0)
1246
1320
  throw new Error("Cannot set value: empty path");
1247
- let a = e;
1248
- for (let o = 0; o < s.length - 1; o++) {
1249
- const u = s[o];
1250
- let h = a[u.key];
1251
- if (u.index !== void 0) {
1252
- if (!Array.isArray(h))
1253
- throw new Error(`Expected array at path segment: ${u.key}, but got ${typeof h}`);
1254
- a = h[u.index];
1321
+ let r = e;
1322
+ for (let o = 0; o < i.length - 1; o++) {
1323
+ const l = i[o];
1324
+ let u = r[l.key];
1325
+ if (l.index !== void 0) {
1326
+ if (!Array.isArray(u))
1327
+ throw new Error(`Expected array at path segment: ${l.key}, but got ${typeof u}`);
1328
+ r = u[l.index];
1255
1329
  } else
1256
- h == null && (a[u.key] = {}, h = a[u.key]), a = h;
1330
+ u == null && (r[l.key] = {}, u = r[l.key]), r = u;
1257
1331
  }
1258
- const n = s[s.length - 1];
1332
+ const n = i[i.length - 1];
1259
1333
  if (n.index !== void 0) {
1260
- const o = a[n.key];
1334
+ const o = r[n.key];
1261
1335
  if (!Array.isArray(o))
1262
1336
  throw new Error(
1263
1337
  `Expected array at path segment: ${n.key}, but got ${typeof o}`
1264
1338
  );
1265
- o[n.index] = i;
1339
+ o[n.index] = s;
1266
1340
  } else
1267
- a[n.key] = i;
1341
+ r[n.key] = s;
1268
1342
  }
1269
1343
  /**
1270
1344
  * Получить значение из FormNode по пути
@@ -1301,9 +1375,9 @@ class O {
1301
1375
  * ```
1302
1376
  */
1303
1377
  getFormNodeValue(e, t) {
1304
- const i = this.getNodeByPath(e, t);
1305
- if (i != null)
1306
- return this.isFormNode(i) ? i.value.value : i;
1378
+ const s = this.getNodeByPath(e, t);
1379
+ if (s != null)
1380
+ return this.isFormNode(s) ? s.value.value : s;
1307
1381
  }
1308
1382
  /**
1309
1383
  * Type guard для проверки, является ли объект FormNode
@@ -1361,560 +1435,224 @@ class O {
1361
1435
  * ```
1362
1436
  */
1363
1437
  getNodeByPath(e, t) {
1364
- const i = this.parsePath(t);
1365
- let s = e;
1366
- for (const a of i) {
1367
- if (s == null) return null;
1368
- const n = s;
1438
+ const s = this.parsePath(t);
1439
+ let i = e;
1440
+ for (const r of s) {
1441
+ if (i == null) return null;
1442
+ const n = i;
1369
1443
  if (n.fields && n.fields instanceof Map) {
1370
- if (s = n.fields.get(a.key), a.index === void 0) {
1371
- if (s == null) return null;
1444
+ if (i = n.fields.get(r.key), r.index === void 0) {
1445
+ if (i == null) return null;
1372
1446
  continue;
1373
1447
  }
1374
- } else if (a.index !== void 0 && n.items) {
1448
+ } else if (r.index !== void 0 && n.items) {
1375
1449
  const o = n.items.value || n.items;
1376
- if (!Array.isArray(o) || (s = o[a.index], s == null)) return null;
1450
+ if (!Array.isArray(o) || (i = o[r.index], i == null)) return null;
1377
1451
  continue;
1378
- } else if (a.index === void 0) {
1379
- if (s = n[a.key], s == null) return null;
1452
+ } else if (r.index === void 0) {
1453
+ if (i = n[r.key], i == null) return null;
1380
1454
  continue;
1381
1455
  }
1382
- if (s && a.index !== void 0 && s.items) {
1383
- const o = s.items.value || s.items;
1456
+ if (i && r.index !== void 0 && i.items) {
1457
+ const o = i.items.value || i.items;
1384
1458
  if (!Array.isArray(o)) return null;
1385
- s = o[a.index];
1386
- } else if (s && a.index !== void 0 && !s.items)
1459
+ i = o[r.index];
1460
+ } else if (i && r.index !== void 0 && !i.items)
1387
1461
  return null;
1388
- if (s == null) return null;
1462
+ if (i == null) return null;
1389
1463
  }
1390
- return s;
1464
+ return i;
1391
1465
  }
1392
1466
  }
1393
- class $ {
1394
- /**
1395
- * Внутреннее хранилище полей
1396
- * Map обеспечивает быструю lookup производительность O(1)
1397
- */
1398
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1399
- fields = /* @__PURE__ */ new Map();
1400
- /**
1401
- * Установить поле в реестр
1402
- *
1403
- * @param key - Ключ поля (имя свойства в типе T)
1404
- * @param node - FormNode для этого поля
1405
- *
1406
- * @example
1407
- * ```typescript
1408
- * registry.set('email', new FieldNode({ value: '' }));
1409
- * ```
1410
- */
1411
- set(e, t) {
1412
- this.fields.set(e, t);
1413
- }
1414
- /**
1415
- * Получить поле из реестра
1416
- *
1417
- * @param key - Ключ поля
1418
- * @returns FormNode или undefined, если поле не найдено
1419
- *
1420
- * @example
1421
- * ```typescript
1422
- * const emailField = registry.get('email');
1423
- * if (emailField) {
1424
- * console.log(emailField.value.value);
1425
- * }
1426
- * ```
1427
- */
1428
- get(e) {
1429
- return this.fields.get(e);
1430
- }
1431
- /**
1432
- * Проверить наличие поля в реестре
1433
- *
1434
- * @param key - Ключ поля
1435
- * @returns true если поле существует
1436
- *
1437
- * @example
1438
- * ```typescript
1439
- * if (registry.has('email')) {
1440
- * console.log('Email field exists');
1441
- * }
1442
- * ```
1443
- */
1444
- has(e) {
1445
- return this.fields.has(e);
1446
- }
1447
- /**
1448
- * Удалить поле из реестра
1449
- *
1450
- * @param key - Ключ поля
1451
- * @returns true если поле было удалено, false если поля не было
1452
- *
1453
- * @example
1454
- * ```typescript
1455
- * registry.delete('email');
1456
- * ```
1457
- */
1458
- delete(e) {
1459
- return this.fields.delete(e);
1460
- }
1461
- /**
1462
- * Перебрать все поля
1463
- *
1464
- * @param callback - Функция обратного вызова для каждого поля
1465
- *
1466
- * @example
1467
- * ```typescript
1468
- * registry.forEach((field, key) => {
1469
- * console.log(`${key}: ${field.value.value}`);
1470
- * });
1471
- * ```
1472
- */
1473
- forEach(e) {
1474
- this.fields.forEach(e);
1475
- }
1476
- /**
1477
- * Получить итератор значений (полей)
1478
- *
1479
- * @returns Итератор по всем полям
1480
- *
1481
- * @example
1482
- * ```typescript
1483
- * for (const field of registry.values()) {
1484
- * await field.validate();
1485
- * }
1486
- * ```
1487
- */
1488
- values() {
1489
- return this.fields.values();
1490
- }
1491
- /**
1492
- * Получить итератор пар [ключ, значение]
1493
- *
1494
- * @returns Итератор по всем записям
1495
- *
1496
- * @example
1497
- * ```typescript
1498
- * for (const [key, field] of registry.entries()) {
1499
- * console.log(key, field.value.value);
1500
- * }
1501
- * ```
1502
- */
1503
- entries() {
1504
- return this.fields.entries();
1505
- }
1506
- /**
1507
- * Получить итератор ключей полей
1508
- *
1509
- * @returns Итератор по всем ключам
1510
- *
1511
- * @example
1512
- * ```typescript
1513
- * const fieldNames = Array.from(registry.keys());
1514
- * // ['email', 'name', 'age']
1515
- * ```
1516
- */
1517
- keys() {
1518
- return this.fields.keys();
1519
- }
1520
- /**
1521
- * Получить количество полей
1522
- *
1523
- * @returns Количество зарегистрированных полей
1524
- *
1525
- * @example
1526
- * ```typescript
1527
- * console.log(`Form has ${registry.size()} fields`);
1528
- * ```
1529
- */
1530
- size() {
1531
- return this.fields.size;
1532
- }
1533
- /**
1534
- * Очистить все поля
1535
- *
1536
- * Удаляет все поля из реестра
1537
- *
1538
- * @example
1539
- * ```typescript
1540
- * registry.clear();
1541
- * console.log(registry.size()); // 0
1542
- * ```
1543
- */
1544
- clear() {
1545
- this.fields.clear();
1546
- }
1547
- /**
1548
- * Получить все поля как массив
1549
- *
1550
- * Полезно для операций, требующих работу с массивом
1551
- *
1552
- * @returns Массив всех полей
1553
- *
1554
- * @example
1555
- * ```typescript
1556
- * const allValid = registry.toArray().every(field => field.valid.value);
1557
- * ```
1558
- */
1559
- toArray() {
1560
- return Array.from(this.fields.values());
1561
- }
1562
- /**
1563
- * Получить Map-представление реестра (readonly)
1564
- *
1565
- * Используйте для совместимости с существующим кодом
1566
- *
1567
- * @returns ReadonlyMap с полями
1568
- * @internal
1569
- *
1570
- * @example
1571
- * ```typescript
1572
- * const mapView = registry.asMap();
1573
- * ```
1574
- */
1575
- asMap() {
1576
- return this.fields;
1577
- }
1467
+ function W(a) {
1468
+ const { getChildren: e, ownErrors: t, disabled: s } = a, i = d(() => t.value.length > 0 ? !1 : e().every((h) => h.disabled.value || h.valid.value)), r = d(() => !i.value), n = d(() => e().some((h) => h.pending.value)), o = d(() => e().some((h) => h.touched.value)), l = d(() => e().some((h) => h.dirty.value)), u = d(() => {
1469
+ const h = [...t.value];
1470
+ for (const f of e())
1471
+ h.push(...f.errors.value);
1472
+ return h;
1473
+ }), v = d(() => s?.value ? "disabled" : n.value ? "pending" : r.value ? "invalid" : "valid");
1474
+ return {
1475
+ valid: i,
1476
+ invalid: r,
1477
+ pending: n,
1478
+ touched: o,
1479
+ dirty: l,
1480
+ errors: u,
1481
+ status: v
1482
+ };
1578
1483
  }
1579
- class G {
1484
+ function ie(a) {
1485
+ return typeof a == "object" && a !== null && "getProxy" in a && typeof a.getProxy == "function";
1486
+ }
1487
+ function re(a, e) {
1488
+ return new Proxy(a, {
1489
+ get: (t, s) => {
1490
+ if (s in t)
1491
+ return t[s];
1492
+ if (typeof s == "string" && e.has(s)) {
1493
+ const i = e.get(s);
1494
+ return i && ie(i) ? i.getProxy() : i;
1495
+ }
1496
+ },
1497
+ set: (t, s, i) => typeof s == "string" && e.has(s) ? !1 : (t[s] = i, !0),
1498
+ has: (t, s) => typeof s == "string" && e.has(s) ? !0 : s in t,
1499
+ ownKeys: (t) => {
1500
+ const s = Reflect.ownKeys(t), i = Array.from(e.keys());
1501
+ return [.../* @__PURE__ */ new Set([...s, ...i])];
1502
+ },
1503
+ getOwnPropertyDescriptor: (t, s) => typeof s == "string" && e.has(s) ? { enumerable: !0, configurable: !0 } : Reflect.getOwnPropertyDescriptor(t, s)
1504
+ });
1505
+ }
1506
+ class ae {
1580
1507
  /**
1581
- * @param fieldRegistry - Реестр полей для доступа к коллекции
1508
+ * @param form - Форма для отправки
1582
1509
  */
1583
1510
  constructor(e) {
1584
- this.fieldRegistry = e;
1511
+ this.form = e;
1585
1512
  }
1513
+ /** Внутренний сигнал состояния отправки */
1514
+ _submitting = g(!1);
1515
+ /** Публичный read-only сигнал состояния отправки */
1516
+ submitting = d(() => this._submitting.value);
1586
1517
  /**
1587
- * Создать Proxy для GroupNode
1518
+ * Отправить форму
1588
1519
  *
1589
- * Proxy позволяет обращаться к полям формы напрямую:
1590
- * - form.email вместо form.fields.get('email')
1591
- * - form.address.city вместо form.fields.get('address').fields.get('city')
1520
+ * Процесс:
1521
+ * 1. Помечает все поля как touched (для отображения ошибок)
1522
+ * 2. Валидирует форму
1523
+ * 3. Если валидация успешна - вызывает onSubmit
1524
+ * 4. Управляет состоянием submitting
1592
1525
  *
1593
- * @param target - GroupNode для которого создается Proxy
1594
- * @returns Proxy с типобезопасным доступом к полям
1526
+ * @param onSubmit - Callback для отправки данных
1527
+ * @param options - Опции submit
1528
+ * @returns Результат от onSubmit или null если валидация не пройдена
1595
1529
  *
1596
1530
  * @example
1597
1531
  * ```typescript
1598
- * const proxy = proxyBuilder.build(groupNode);
1599
- *
1600
- * // Доступ к полям
1601
- * console.log(proxy.email.value); // Работает!
1602
- * console.log(proxy.name.value); // Работает!
1603
- *
1604
- * // Доступ к методам GroupNode
1605
- * await proxy.validate(); // Работает!
1606
- * proxy.markAsTouched(); // Работает!
1607
- *
1608
- * // Проверка существования
1609
- * if ('email' in proxy) { ... }
1532
+ * const result = await submitter.submit(async (values) => {
1533
+ * const response = await fetch('/api/form', {
1534
+ * method: 'POST',
1535
+ * body: JSON.stringify(values)
1536
+ * });
1537
+ * return response.json();
1538
+ * });
1610
1539
  *
1611
- * // Перечисление ключей
1612
- * Object.keys(proxy); // ['email', 'name', ...]
1540
+ * if (result === null) {
1541
+ * console.log('Форма не прошла валидацию');
1542
+ * }
1613
1543
  * ```
1614
1544
  */
1615
- build(e) {
1616
- return new Proxy(e, {
1617
- /**
1618
- * Get trap: Перехват доступа к свойствам
1619
- *
1620
- * Приоритет:
1621
- * 1. Собственные свойства и методы GroupNode (validate, setValue и т.д.)
1622
- * 2. Поля формы из fieldRegistry
1623
- * 3. undefined для несуществующих свойств
1624
- */
1625
- get: (t, i) => {
1626
- if (i in t)
1627
- return t[i];
1628
- if (typeof i == "string" && this.fieldRegistry.has(i))
1629
- return this.fieldRegistry.get(i);
1630
- },
1631
- /**
1632
- * Set trap: Перехват установки свойств
1633
- *
1634
- * Запрещает прямую установку значений полей через form.email = value
1635
- * Пользователь должен использовать form.email.setValue(value) или form.setValue({...})
1636
- */
1637
- set: (t, i, s) => typeof i == "string" && this.fieldRegistry.has(i) ? !1 : (t[i] = s, !0),
1638
- /**
1639
- * Has trap: Перехват оператора 'in'
1640
- *
1641
- * Позволяет проверять существование полей:
1642
- * if ('email' in form) { ... }
1643
- */
1644
- has: (t, i) => typeof i == "string" && this.fieldRegistry.has(i) ? !0 : i in t,
1645
- /**
1646
- * OwnKeys trap: Перехват Object.keys() / Object.getOwnPropertyNames()
1647
- *
1648
- * Возвращает объединенный список:
1649
- * - Ключей самого GroupNode
1650
- * - Ключей полей из fieldRegistry
1651
- */
1652
- ownKeys: (t) => {
1653
- const i = Reflect.ownKeys(t), s = Array.from(this.fieldRegistry.keys());
1654
- return [.../* @__PURE__ */ new Set([...i, ...s])];
1655
- },
1656
- /**
1657
- * GetOwnPropertyDescriptor trap: Перехват Object.getOwnPropertyDescriptor()
1658
- *
1659
- * Возвращает дескриптор свойства для полей и свойств GroupNode
1660
- * Важно для корректной работы Object.keys() и других рефлексивных операций
1661
- */
1662
- getOwnPropertyDescriptor: (t, i) => typeof i == "string" && this.fieldRegistry.has(i) ? {
1663
- enumerable: !0,
1664
- // Поле должно быть перечисляемым
1665
- configurable: !0
1666
- // Поле может быть удалено
1667
- // Не указываем writable, т.к. это accessor property через get/set traps
1668
- } : Reflect.getOwnPropertyDescriptor(t, i)
1669
- });
1545
+ async submit(e, t) {
1546
+ const { skipValidation: s = !1, skipTouch: i = !1 } = t || {};
1547
+ if (i || this.form.markAsTouched(), !s && !await this.form.validate())
1548
+ return null;
1549
+ this._submitting.value = !0;
1550
+ try {
1551
+ return await e(this.form.getValue());
1552
+ } finally {
1553
+ this._submitting.value = !1;
1554
+ }
1670
1555
  }
1671
- }
1672
- class U {
1673
- // ============================================================================
1674
- // Конструктор
1675
- // ============================================================================
1676
1556
  /**
1677
- * Создать менеджер состояния
1557
+ * Отправить форму с расширенным результатом
1678
1558
  *
1679
- * @param fieldRegistry - реестр полей формы
1680
- */
1681
- constructor(e) {
1682
- this.fieldRegistry = e, this._submitting = d(!1), this._disabled = d(!1), this._formErrors = d([]), this.value = l(() => {
1683
- const t = {};
1684
- return this.fieldRegistry.forEach((i, s) => {
1685
- t[s] = i.value.value;
1686
- }), t;
1687
- }), this.valid = l(() => this._formErrors.value.length > 0 ? !1 : Array.from(this.fieldRegistry.values()).every((i) => i.valid.value)), this.invalid = l(() => !this.valid.value), this.pending = l(
1688
- () => Array.from(this.fieldRegistry.values()).some((t) => t.pending.value)
1689
- ), this.touched = l(
1690
- () => Array.from(this.fieldRegistry.values()).some((t) => t.touched.value)
1691
- ), this.dirty = l(
1692
- () => Array.from(this.fieldRegistry.values()).some((t) => t.dirty.value)
1693
- ), this.errors = l(() => {
1694
- const t = [];
1695
- return t.push(...this._formErrors.value), this.fieldRegistry.forEach((i) => {
1696
- t.push(...i.errors.value);
1697
- }), t;
1698
- }), this.status = l(() => this._disabled.value ? "disabled" : this.pending.value ? "pending" : this.invalid.value ? "invalid" : "valid"), this.submitting = l(() => this._submitting.value);
1699
- }
1700
- // ============================================================================
1701
- // Приватные сигналы (мутабельные)
1702
- // ============================================================================
1703
- /**
1704
- * Флаг отправки формы
1705
- * Устанавливается в true во время отправки формы на сервер
1706
- */
1707
- _submitting;
1708
- /**
1709
- * Флаг disabled состояния
1710
- * Если true, форма считается disabled
1711
- */
1712
- _disabled;
1713
- /**
1714
- * Form-level validation errors (не связанные с конкретным полем)
1715
- * Используется для server-side errors или кросс-полевой валидации
1716
- */
1717
- _formErrors;
1718
- // ============================================================================
1719
- // Публичные computed signals (read-only)
1720
- // ============================================================================
1721
- /**
1722
- * Значение формы как объект
1559
+ * В отличие от submit(), возвращает объект с информацией об успехе/ошибке
1723
1560
  *
1724
- * Computed signal, который автоматически пересчитывается при изменении любого поля.
1725
- * Использует мемоизацию - если зависимости не изменились, вернет закешированный объект.
1561
+ * @param onSubmit - Callback для отправки данных
1562
+ * @param options - Опции submit
1563
+ * @returns Объект SubmitResult с данными и статусом
1726
1564
  *
1727
1565
  * @example
1728
1566
  * ```typescript
1729
- * const form = new GroupNode({ email: { value: 'test@mail.com' } });
1730
- * console.log(form.value.value); // { email: 'test@mail.com' }
1731
- * ```
1732
- */
1733
- value;
1734
- /**
1735
- * Форма валидна?
1736
- *
1737
- * Computed signal. Форма валидна, если:
1738
- * - Нет form-level errors
1739
- * - Все поля валидны
1740
- */
1741
- valid;
1742
- /**
1743
- * Форма невалидна?
1744
- *
1745
- * Computed signal. Инверсия valid.
1746
- */
1747
- invalid;
1748
- /**
1749
- * Хотя бы одно поле touched?
1750
- *
1751
- * Computed signal. Возвращает true, если хотя бы одно поле было touched.
1752
- */
1753
- touched;
1754
- /**
1755
- * Хотя бы одно поле dirty?
1756
- *
1757
- * Computed signal. Возвращает true, если хотя бы одно поле изменилось.
1758
- */
1759
- dirty;
1760
- /**
1761
- * Асинхронная валидация в процессе?
1762
- *
1763
- * Computed signal. Возвращает true, если хотя бы одно поле находится в pending состоянии.
1764
- */
1765
- pending;
1766
- /**
1767
- * Все ошибки валидации
1768
- *
1769
- * Computed signal. Возвращает массив всех ошибок:
1770
- * - Form-level errors
1771
- * - Field-level errors (из всех вложенных полей)
1772
- */
1773
- errors;
1774
- /**
1775
- * Общий статус формы
1776
- *
1777
- * Computed signal. Возможные значения:
1778
- * - 'disabled' - форма disabled
1779
- * - 'pending' - асинхронная валидация в процессе
1780
- * - 'invalid' - форма невалидна
1781
- * - 'valid' - форма валидна
1782
- */
1783
- status;
1784
- /**
1785
- * Форма в процессе отправки?
1786
- *
1787
- * Computed signal (обертка над _submitting для read-only доступа).
1788
- */
1789
- submitting;
1790
- // ============================================================================
1791
- // Публичные методы для управления состоянием
1792
- // ============================================================================
1793
- /**
1794
- * Установить form-level ошибки
1795
- *
1796
- * @param errors - массив ошибок валидации
1797
- *
1798
- * @example
1799
- * ```typescript
1800
- * // Server-side ошибки
1801
- * stateManager.setFormErrors([
1802
- * { code: 'server_error', message: 'Пользователь с таким email уже существует' }
1803
- * ]);
1804
- * ```
1805
- */
1806
- setFormErrors(e) {
1807
- this._formErrors.value = e;
1808
- }
1809
- /**
1810
- * Очистить form-level ошибки
1811
- */
1812
- clearFormErrors() {
1813
- this._formErrors.value = [];
1814
- }
1815
- /**
1816
- * Получить form-level ошибки
1817
- */
1818
- getFormErrors() {
1819
- return this._formErrors.value;
1820
- }
1821
- /**
1822
- * Установить флаг submitting
1823
- *
1824
- * @param value - true если форма отправляется, false если нет
1567
+ * const result = await submitter.submitWithResult(async (values) => {
1568
+ * return await api.saveForm(values);
1569
+ * });
1825
1570
  *
1826
- * @example
1827
- * ```typescript
1828
- * stateManager.setSubmitting(true);
1829
- * await api.submitForm(form.getValue());
1830
- * stateManager.setSubmitting(false);
1571
+ * if (result.success) {
1572
+ * console.log('Сохранено:', result.data);
1573
+ * } else if (result.error) {
1574
+ * console.error('Ошибка:', result.error.message);
1575
+ * } else {
1576
+ * console.log('Валидация не пройдена');
1577
+ * }
1831
1578
  * ```
1832
1579
  */
1833
- setSubmitting(e) {
1834
- this._submitting.value = e;
1835
- }
1836
- /**
1837
- * Установить флаг disabled
1838
- *
1839
- * @param value - true если форма disabled, false если нет
1840
- */
1841
- setDisabled(e) {
1842
- this._disabled.value = e;
1580
+ async submitWithResult(e, t) {
1581
+ const { skipValidation: s = !1, skipTouch: i = !1 } = t || {};
1582
+ if (i || this.form.markAsTouched(), !s && !await this.form.validate())
1583
+ return { success: !1, data: null };
1584
+ this._submitting.value = !0;
1585
+ try {
1586
+ return { success: !0, data: await e(this.form.getValue()) };
1587
+ } catch (r) {
1588
+ return {
1589
+ success: !1,
1590
+ data: null,
1591
+ error: r instanceof Error ? r : new Error(String(r))
1592
+ };
1593
+ } finally {
1594
+ this._submitting.value = !1;
1595
+ }
1843
1596
  }
1844
1597
  /**
1845
- * Получить флаг disabled
1598
+ * Проверить, идет ли отправка формы
1846
1599
  */
1847
- isDisabled() {
1848
- return this._disabled.value;
1600
+ isSubmitting() {
1601
+ return this._submitting.value;
1849
1602
  }
1850
1603
  }
1851
- class m extends A {
1604
+ class k extends C {
1852
1605
  // ============================================================================
1853
1606
  // Приватные поля
1854
1607
  // ============================================================================
1855
- id = D();
1856
- /**
1857
- * Реестр полей формы
1858
- * Использует FieldRegistry для инкапсуляции логики управления коллекцией полей
1859
- */
1860
- fieldRegistry;
1861
- /**
1862
- * Строитель Proxy для типобезопасного доступа к полям
1863
- * Использует ProxyBuilder для создания Proxy с расширенной функциональностью
1864
- */
1865
- proxyBuilder;
1608
+ id = crypto.randomUUID();
1866
1609
  /**
1867
- * Менеджер состояния формы
1868
- * Инкапсулирует всю логику создания и управления сигналами состояния
1869
- * Извлечен из GroupNode для соблюдения SRP
1610
+ * Коллекция полей формы (упрощённый Map вместо FieldRegistry)
1870
1611
  */
1871
- stateManager;
1612
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1613
+ _fields = /* @__PURE__ */ new Map();
1872
1614
  /**
1873
1615
  * Менеджер подписок для централизованного cleanup
1874
- * Использует SubscriptionManager вместо массива для управления подписками
1875
1616
  */
1876
- disposers = new _();
1617
+ disposers = new D();
1877
1618
  /**
1878
1619
  * Ссылка на Proxy-инстанс для использования в BehaviorContext
1879
- * Устанавливается в конструкторе до применения behavior schema
1880
1620
  */
1881
1621
  _proxyInstance;
1882
1622
  /**
1883
1623
  * Навигатор для работы с путями к полям
1884
- * Использует композицию вместо дублирования логики парсинга путей
1885
1624
  */
1886
- pathNavigator = new O();
1625
+ pathNavigator = new se();
1887
1626
  /**
1888
1627
  * Фабрика для создания узлов формы
1889
- * Использует композицию для централизованного создания FieldNode/GroupNode/ArrayNode
1890
1628
  */
1891
- nodeFactory = new L();
1629
+ nodeFactory = new ne();
1892
1630
  /**
1893
1631
  * Реестр валидаторов для этой формы
1894
- * Использует композицию вместо глобального Singleton
1895
- * Обеспечивает полную изоляцию форм друг от друга
1632
+ * Может быть инжектирован через config._validationRegistry для тестирования
1896
1633
  */
1897
- validationRegistry = new C();
1634
+ validationRegistry;
1898
1635
  /**
1899
1636
  * Реестр behaviors для этой формы
1900
- * Использует композицию вместо глобального Singleton
1901
- * Обеспечивает полную изоляцию форм друг от друга
1637
+ * Может быть инжектирован через config._behaviorRegistry для тестирования
1902
1638
  */
1903
- behaviorRegistry = new T();
1639
+ behaviorRegistry;
1904
1640
  /**
1905
1641
  * Аппликатор для применения валидаторов к форме
1906
- * Извлечен из GroupNode для соблюдения SRP
1907
- * Использует композицию для управления процессом валидации
1908
- */
1909
- validationApplicator = new N(this);
1910
- /**
1911
- * Аппликатор для применения behavior схемы к форме
1912
- * Извлечен из GroupNode для соблюдения SRP
1913
- * Использует композицию для управления процессом применения behaviors
1914
1642
  */
1915
- behaviorApplicator = new B(this, this.behaviorRegistry);
1643
+ validationApplicator = new te(this);
1916
1644
  // ============================================================================
1917
- // Публичные computed signals (делегированы в StateManager)
1645
+ // Приватные сигналы состояния (inline из StateManager)
1646
+ // ============================================================================
1647
+ /** Управление отправкой формы */
1648
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1649
+ formSubmitter;
1650
+ /** Флаг disabled состояния */
1651
+ _disabled = g(!1);
1652
+ /** Form-level validation errors */
1653
+ _formErrors = g([]);
1654
+ // ============================================================================
1655
+ // Публичные computed signals
1918
1656
  // ============================================================================
1919
1657
  value;
1920
1658
  valid;
@@ -1926,36 +1664,58 @@ class m extends A {
1926
1664
  status;
1927
1665
  submitting;
1928
1666
  constructor(e) {
1929
- super(), this.fieldRegistry = new $(), this.proxyBuilder = new G(this.fieldRegistry);
1930
- const t = "form" in e, i = t ? e.form : e, s = t ? e.behavior : void 0, a = t ? e.validation : void 0;
1931
- for (const [o, u] of Object.entries(i)) {
1932
- const h = this.createNode(u);
1933
- this.fieldRegistry.set(o, h);
1667
+ super(), this.formSubmitter = new ae(this);
1668
+ const t = "form" in e, s = t ? e : void 0, i = t ? e.form : e, r = t ? e.behavior : void 0, n = t ? e.validation : void 0;
1669
+ this.validationRegistry = s?._validationRegistry ?? new K(), this.behaviorRegistry = s?._behaviorRegistry ?? new H();
1670
+ for (const [l, u] of Object.entries(i)) {
1671
+ const v = this.createNode(u);
1672
+ this._fields.set(l, v);
1934
1673
  }
1935
- this.stateManager = new U(this.fieldRegistry), this.value = this.stateManager.value, this.valid = this.stateManager.valid, this.invalid = this.stateManager.invalid, this.touched = this.stateManager.touched, this.dirty = this.stateManager.dirty, this.pending = this.stateManager.pending, this.errors = this.stateManager.errors, this.status = this.stateManager.status, this.submitting = this.stateManager.submitting;
1936
- const n = this.proxyBuilder.build(this);
1937
- return this._proxyInstance = n, s && this.applyBehaviorSchema(s), a && this.applyValidationSchema(a), n;
1674
+ this.value = d(() => {
1675
+ const l = {};
1676
+ return this._fields.forEach((u, v) => {
1677
+ l[v] = u.value.value;
1678
+ }), l;
1679
+ });
1680
+ const o = W({
1681
+ getChildren: () => Array.from(this._fields.values()),
1682
+ ownErrors: this._formErrors,
1683
+ disabled: this._disabled
1684
+ });
1685
+ this.valid = o.valid, this.invalid = o.invalid, this.pending = o.pending, this.touched = o.touched, this.dirty = o.dirty, this.errors = o.errors, this.status = o.status, this.submitting = this.formSubmitter.submitting, r && this.applyBehaviorSchema(r), n && this.applyValidationSchema(n);
1686
+ }
1687
+ // ============================================================================
1688
+ // Приватный метод для создания Proxy
1689
+ // ============================================================================
1690
+ /**
1691
+ * Создать Proxy для типобезопасного доступа к полям
1692
+ * @see buildFormProxy
1693
+ */
1694
+ buildProxy() {
1695
+ return re(this, this._fields);
1938
1696
  }
1939
1697
  // ============================================================================
1940
1698
  // Реализация абстрактных методов FormNode
1941
1699
  // ============================================================================
1942
1700
  getValue() {
1943
1701
  const e = {};
1944
- return this.fieldRegistry.forEach((t, i) => {
1945
- e[i] = t.getValue();
1702
+ return this._fields.forEach((t, s) => {
1703
+ e[s] = t.getValue();
1946
1704
  }), e;
1947
1705
  }
1948
1706
  setValue(e, t) {
1949
- for (const [i, s] of Object.entries(e)) {
1950
- const a = this.fieldRegistry.get(i);
1951
- a && a.setValue(s, t);
1707
+ for (const [s, i] of Object.entries(e)) {
1708
+ const r = this._fields.get(s);
1709
+ r && r.setValue(i, t);
1952
1710
  }
1953
1711
  }
1954
1712
  patchValue(e) {
1955
- for (const [t, i] of Object.entries(e)) {
1956
- const s = this.fieldRegistry.get(t);
1957
- s && i !== void 0 && s.setValue(i);
1958
- }
1713
+ j(() => {
1714
+ for (const [t, s] of Object.entries(e)) {
1715
+ const i = this._fields.get(t);
1716
+ i && s !== void 0 && i.setValue(s, { emitEvent: !1 });
1717
+ }
1718
+ });
1959
1719
  }
1960
1720
  /**
1961
1721
  * Сбросить форму к указанным значениям (или к initialValues)
@@ -1975,103 +1735,49 @@ class m extends A {
1975
1735
  * ```
1976
1736
  */
1977
1737
  reset(e) {
1978
- this.fieldRegistry.forEach((t, i) => {
1979
- const s = e?.[i];
1980
- t.reset(s);
1738
+ this._fields.forEach((t, s) => {
1739
+ const i = e?.[s];
1740
+ t.reset(i);
1981
1741
  });
1982
1742
  }
1983
1743
  /**
1984
1744
  * Сбросить форму к исходным значениям (initialValues)
1985
- *
1986
- * @remarks
1987
- * Рекурсивно вызывает resetToInitial() для всех полей формы.
1988
- * Более явный способ сброса к начальным значениям по сравнению с reset()
1989
- *
1990
- * Полезно когда:
1991
- * - Пользователь нажал "Cancel" - полная отмена изменений
1992
- * - Форма была изменена через reset(newValues), но нужно вернуться к самому началу
1993
- * - Явное намерение показать "отмена всех изменений"
1994
- *
1995
- * @example
1996
- * ```typescript
1997
- * const form = new GroupNode({
1998
- * email: { value: 'initial@mail.com', component: Input },
1999
- * name: { value: 'John', component: Input }
2000
- * });
2001
- *
2002
- * form.email.setValue('changed@mail.com');
2003
- * form.reset({ email: 'temp@mail.com', name: 'Jane' });
2004
- * console.log(form.getValue()); // { email: 'temp@mail.com', name: 'Jane' }
2005
- *
2006
- * form.resetToInitial();
2007
- * console.log(form.getValue()); // { email: 'initial@mail.com', name: 'John' }
2008
- * ```
2009
1745
  */
2010
1746
  resetToInitial() {
2011
- this.fieldRegistry.forEach((e) => {
1747
+ this._fields.forEach((e) => {
2012
1748
  "resetToInitial" in e && typeof e.resetToInitial == "function" ? e.resetToInitial() : e.reset();
2013
1749
  });
2014
1750
  }
2015
1751
  async validate() {
2016
- this.clearErrors(), await Promise.all(Array.from(this.fieldRegistry.values()).map((t) => t.validate()));
1752
+ this.clearErrors(), await Promise.all(Array.from(this._fields.values()).map((t) => t.validate()));
2017
1753
  const e = this.validationRegistry.getValidators();
2018
- return e && e.length > 0 && await this.applyContextualValidators(e), Array.from(this.fieldRegistry.values()).every((t) => t.valid.value);
1754
+ return e && e.length > 0 && await this.applyContextualValidators(e), Array.from(this._fields.values()).every(
1755
+ (t) => t.valid.value || t.disabled.value
1756
+ );
2019
1757
  }
2020
1758
  /**
2021
1759
  * Установить form-level validation errors
2022
- * Используется для server-side validation или кросс-полевых ошибок
2023
- *
2024
- * @param errors - массив ошибок уровня формы
2025
- *
2026
- * @example
2027
- * ```typescript
2028
- * // Server-side validation после submit
2029
- * try {
2030
- * await api.createUser(form.getValue());
2031
- * } catch (error) {
2032
- * form.setErrors([
2033
- * { code: 'duplicate_email', message: 'Email уже используется' }
2034
- * ]);
2035
- * }
2036
- * ```
2037
1760
  */
2038
1761
  setErrors(e) {
2039
- this.stateManager.setFormErrors(e);
1762
+ this._formErrors.value = e;
2040
1763
  }
2041
1764
  /**
2042
1765
  * Очистить все errors (form-level + field-level)
2043
1766
  */
2044
1767
  clearErrors() {
2045
- this.stateManager.clearFormErrors(), this.fieldRegistry.forEach((e) => e.clearErrors());
1768
+ this._formErrors.value = [], this._fields.forEach((e) => e.clearErrors());
2046
1769
  }
2047
1770
  /**
2048
1771
  * Получить поле по ключу
2049
- *
2050
- * Публичный метод для доступа к полю из fieldRegistry
2051
- *
2052
- * @param key - Ключ поля
2053
- * @returns FormNode или undefined, если поле не найдено
2054
- *
2055
- * @example
2056
- * ```typescript
2057
- * const emailField = form.getField('email');
2058
- * if (emailField) {
2059
- * console.log(emailField.value.value);
2060
- * }
2061
- * ```
2062
1772
  */
2063
1773
  getField(e) {
2064
- return this.fieldRegistry.get(e);
1774
+ return this._fields.get(e);
2065
1775
  }
2066
1776
  /**
2067
- * Получить Map всех полей формы
2068
- *
2069
- * Используется в FieldPathNavigator для навигации по полям
2070
- *
2071
- * @returns Map полей формы
1777
+ * Получить Map всех полей формы (для совместимости)
2072
1778
  */
2073
1779
  get fields() {
2074
- return this.fieldRegistry;
1780
+ return this._fields;
2075
1781
  }
2076
1782
  /**
2077
1783
  * Получить Proxy-инстанс для прямого доступа к полям
@@ -2100,77 +1806,51 @@ class m extends A {
2100
1806
  * ```
2101
1807
  */
2102
1808
  getProxy() {
2103
- return this._proxyInstance || this;
1809
+ return this._proxyInstance || (this._proxyInstance = this.buildProxy()), this._proxyInstance;
2104
1810
  }
2105
1811
  /**
2106
1812
  * Получить все поля формы как итератор
2107
- *
2108
- * Предоставляет доступ к внутренним полям для валидации и других операций
2109
- *
2110
- * @returns Итератор по всем полям формы
2111
- *
2112
- * @example
2113
- * ```typescript
2114
- * // Валидация всех полей
2115
- * await Promise.all(
2116
- * Array.from(form.getAllFields()).map(field => field.validate())
2117
- * );
2118
- * ```
2119
1813
  */
2120
1814
  getAllFields() {
2121
- return this.fieldRegistry.values();
1815
+ return this._fields.values();
2122
1816
  }
2123
1817
  // ============================================================================
2124
1818
  // Protected hooks (Template Method pattern)
2125
1819
  // ============================================================================
2126
- /**
2127
- * Hook: вызывается после markAsTouched()
2128
- *
2129
- * Для GroupNode: рекурсивно помечаем все дочерние поля как touched
2130
- */
2131
1820
  onMarkAsTouched() {
2132
- this.fieldRegistry.forEach((e) => e.markAsTouched());
1821
+ this._fields.forEach((e) => e.markAsTouched());
2133
1822
  }
2134
- /**
2135
- * Hook: вызывается после markAsUntouched()
2136
- *
2137
- * Для GroupNode: рекурсивно помечаем все дочерние поля как untouched
2138
- */
2139
1823
  onMarkAsUntouched() {
2140
- this.fieldRegistry.forEach((e) => e.markAsUntouched());
1824
+ this._fields.forEach((e) => e.markAsUntouched());
2141
1825
  }
2142
- /**
2143
- * Hook: вызывается после markAsDirty()
2144
- *
2145
- * Для GroupNode: рекурсивно помечаем все дочерние поля как dirty
2146
- */
2147
1826
  onMarkAsDirty() {
2148
- this.fieldRegistry.forEach((e) => e.markAsDirty());
1827
+ this._fields.forEach((e) => e.markAsDirty());
2149
1828
  }
2150
- /**
2151
- * Hook: вызывается после markAsPristine()
2152
- *
2153
- * Для GroupNode: рекурсивно помечаем все дочерние поля как pristine
2154
- */
2155
1829
  onMarkAsPristine() {
2156
- this.fieldRegistry.forEach((e) => e.markAsPristine());
1830
+ this._fields.forEach((e) => e.markAsPristine());
2157
1831
  }
2158
1832
  // ============================================================================
2159
1833
  // Дополнительные методы (из FormStore)
2160
1834
  // ============================================================================
2161
1835
  /**
2162
1836
  * Отправить форму
2163
- * Валидирует форму и вызывает onSubmit если форма валидна
1837
+ *
1838
+ * @param onSubmit - Callback для отправки данных
1839
+ * @param options - Опции submit (skipValidation, skipTouch)
1840
+ * @returns Результат от onSubmit или null если валидация не пройдена
2164
1841
  */
2165
- async submit(e) {
2166
- if (this.markAsTouched(), !await this.validate())
2167
- return null;
2168
- this.stateManager.setSubmitting(!0);
2169
- try {
2170
- return await e(this.getValue());
2171
- } finally {
2172
- this.stateManager.setSubmitting(!1);
2173
- }
1842
+ async submit(e, t) {
1843
+ return this.formSubmitter.submit(e, t);
1844
+ }
1845
+ /**
1846
+ * Отправить форму с расширенным результатом
1847
+ *
1848
+ * @param onSubmit - Callback для отправки данных
1849
+ * @param options - Опции submit
1850
+ * @returns Объект SubmitResult с данными, статусом и возможной ошибкой
1851
+ */
1852
+ async submitWithResult(e, t) {
1853
+ return this.formSubmitter.submitWithResult(e, t);
2174
1854
  }
2175
1855
  /**
2176
1856
  * Применить validation schema к форме
@@ -2181,54 +1861,26 @@ class m extends A {
2181
1861
  applyValidationSchema(e) {
2182
1862
  this.validationRegistry.beginRegistration();
2183
1863
  try {
2184
- const t = x();
1864
+ const t = I();
2185
1865
  e(t);
2186
- const i = this.getProxy();
2187
- this.validationRegistry.endRegistration(i);
1866
+ const s = this.getProxy();
1867
+ this.validationRegistry.endRegistration(s);
2188
1868
  } catch (t) {
2189
1869
  throw console.error("Error applying validation schema:", t), t;
2190
1870
  }
2191
1871
  }
2192
1872
  /**
2193
1873
  * Применить behavior schema к форме
2194
- *
2195
- * ✅ РЕФАКТОРИНГ: Делегирование BehaviorApplicator (SRP)
2196
- *
2197
- * Логика применения behavior схемы извлечена в BehaviorApplicator для:
2198
- * - Соблюдения Single Responsibility Principle
2199
- * - Уменьшения размера GroupNode (~50 строк)
2200
- * - Улучшения тестируемости
2201
- * - Консистентности с ValidationApplicator
2202
- *
2203
- * @param schemaFn Функция описания поведения формы
2204
1874
  * @returns Функция cleanup для отписки от всех behaviors
2205
- *
2206
- * @example
2207
- * ```typescript
2208
- * import { copyFrom, enableWhen, computeFrom } from '@/lib/forms/core/behaviors';
2209
- *
2210
- * const behaviorSchema: BehaviorSchemaFn<MyForm> = (path) => {
2211
- * copyFrom(path.residenceAddress, path.registrationAddress, {
2212
- * when: (form) => form.sameAsRegistration === true
2213
- * });
2214
- *
2215
- * enableWhen(path.propertyValue, (form) => form.loanType === 'mortgage');
2216
- *
2217
- * computeFrom(
2218
- * path.initialPayment,
2219
- * [path.propertyValue],
2220
- * (propertyValue) => propertyValue ? propertyValue * 0.2 : null
2221
- * );
2222
- * };
2223
- *
2224
- * const cleanup = form.applyBehaviorSchema(behaviorSchema);
2225
- *
2226
- * // Cleanup при unmount
2227
- * useEffect(() => cleanup, []);
2228
- * ```
2229
1875
  */
2230
1876
  applyBehaviorSchema(e) {
2231
- return this.behaviorApplicator.apply(e);
1877
+ this.behaviorRegistry.beginRegistration();
1878
+ try {
1879
+ const t = I();
1880
+ return e(t), this.behaviorRegistry.endRegistration(this.getProxy()).cleanup;
1881
+ } catch (t) {
1882
+ throw console.error("Error applying behavior schema:", t), t;
1883
+ }
2232
1884
  }
2233
1885
  /**
2234
1886
  * Получить вложенное поле по пути
@@ -2265,18 +1917,18 @@ class m extends A {
2265
1917
  const t = this.pathNavigator.parsePath(e);
2266
1918
  if (t.length === 0)
2267
1919
  return;
2268
- let i = this;
2269
- for (const s of t) {
2270
- if (!(i instanceof m) || (i = i.getField(s.key), !i)) return;
2271
- if (s.index !== void 0)
2272
- if ("at" in i && "length" in i && typeof i.at == "function") {
2273
- const a = i.at(s.index);
2274
- if (!a) return;
2275
- i = a;
1920
+ let s = this;
1921
+ for (const i of t) {
1922
+ if (!(s instanceof k) || (s = s.getField(i.key), !s)) return;
1923
+ if (i.index !== void 0)
1924
+ if ("at" in s && "length" in s && typeof s.at == "function") {
1925
+ const r = s.at(i.index);
1926
+ if (!r) return;
1927
+ s = r;
2276
1928
  } else
2277
1929
  return;
2278
1930
  }
2279
- return i;
1931
+ return s;
2280
1932
  }
2281
1933
  /**
2282
1934
  * Применить contextual валидаторы к полям
@@ -2319,39 +1971,17 @@ class m extends A {
2319
1971
  // ============================================================================
2320
1972
  /**
2321
1973
  * Связывает два поля: при изменении source автоматически обновляется target
2322
- * Поддерживает опциональную трансформацию значения
2323
- *
2324
- * @param sourceKey - Ключ поля-источника
2325
- * @param targetKey - Ключ поля-цели
2326
- * @param transform - Опциональная функция трансформации значения
2327
- * @returns Функция отписки для cleanup
2328
- *
2329
- * @example
2330
- * ```typescript
2331
- * // Автоматический расчет минимального взноса от стоимости недвижимости
2332
- * const dispose = form.linkFields(
2333
- * 'propertyValue',
2334
- * 'initialPayment',
2335
- * (propertyValue) => propertyValue ? propertyValue * 0.2 : null
2336
- * );
2337
- *
2338
- * // При изменении propertyValue → автоматически обновится initialPayment
2339
- * form.propertyValue.setValue(1000000);
2340
- * // initialPayment станет 200000
2341
- *
2342
- * // Cleanup
2343
- * useEffect(() => dispose, []);
2344
- * ```
2345
1974
  */
2346
- linkFields(e, t, i) {
2347
- const s = this.fieldRegistry.get(e), a = this.fieldRegistry.get(t);
2348
- if (!s || !a)
2349
- return () => {
2350
- };
2351
- const n = v(() => {
2352
- const u = s.value.value, h = i ? i(u) : u;
2353
- a.setValue(h, { emitEvent: !1 });
2354
- }), o = `linkFields-${Date.now()}-${Math.random()}`;
1975
+ linkFields(e, t, s) {
1976
+ const i = this._fields.get(e), r = this._fields.get(t);
1977
+ if (!i || !r) {
1978
+ const l = i ? t : e;
1979
+ throw new Error(`GroupNode.linkFields: field "${String(l)}" not found`);
1980
+ }
1981
+ const n = m(() => {
1982
+ const l = i.value.value, u = s ? s(l) : l;
1983
+ r.setValue(u, { emitEvent: !1 });
1984
+ }), o = A(w.LinkFields);
2355
1985
  return this.disposers.add(o, n);
2356
1986
  }
2357
1987
  /**
@@ -2382,56 +2012,37 @@ class m extends A {
2382
2012
  * ```
2383
2013
  */
2384
2014
  watchField(e, t) {
2385
- const i = this.getFieldByPath(e);
2386
- if (!i)
2387
- return () => {
2388
- };
2389
- const s = v(() => {
2390
- const n = i.value.value;
2015
+ const s = this.getFieldByPath(e);
2016
+ if (!s)
2017
+ throw new Error(`GroupNode.watchField: field "${e}" not found`);
2018
+ const i = m(() => {
2019
+ const n = s.value.value;
2391
2020
  t(n);
2392
- }), a = `watchField-${Date.now()}-${Math.random()}`;
2393
- return this.disposers.add(a, s);
2021
+ }), r = A(w.WatchField);
2022
+ return this.disposers.add(r, i);
2394
2023
  }
2395
2024
  /**
2396
2025
  * Hook: вызывается после disable()
2397
- *
2398
- * Для GroupNode: рекурсивно отключаем все дочерние поля
2399
2026
  */
2400
2027
  onDisable() {
2401
- this.stateManager.setDisabled(!0), this.fieldRegistry.forEach((e) => {
2402
- e.disable();
2403
- });
2028
+ this._disabled.value = !0, this._fields.forEach((e) => e.disable());
2404
2029
  }
2405
2030
  /**
2406
2031
  * Hook: вызывается после enable()
2407
- *
2408
- * Для GroupNode: рекурсивно включаем все дочерние поля
2409
2032
  */
2410
2033
  onEnable() {
2411
- this.stateManager.setDisabled(!1), this.fieldRegistry.forEach((e) => {
2412
- e.enable();
2413
- });
2034
+ this._disabled.value = !1, this._fields.forEach((e) => e.enable());
2414
2035
  }
2415
2036
  /**
2416
2037
  * Очистить все ресурсы узла
2417
- * Рекурсивно очищает все subscriptions и дочерние узлы
2418
- *
2419
- * @example
2420
- * ```typescript
2421
- * useEffect(() => {
2422
- * return () => {
2423
- * form.dispose();
2424
- * };
2425
- * }, []);
2426
- * ```
2427
2038
  */
2428
2039
  dispose() {
2429
- this.disposers.dispose(), this.fieldRegistry.forEach((e) => {
2040
+ this.disposers.dispose(), this._fields.forEach((e) => {
2430
2041
  "dispose" in e && typeof e.dispose == "function" && e.dispose();
2431
2042
  });
2432
2043
  }
2433
2044
  }
2434
- class F extends A {
2045
+ class L extends C {
2435
2046
  // ============================================================================
2436
2047
  // Приватные поля
2437
2048
  // ============================================================================
@@ -2442,7 +2053,9 @@ class F extends A {
2442
2053
  * Менеджер подписок для централизованного cleanup
2443
2054
  * Использует SubscriptionManager вместо массива для управления подписками
2444
2055
  */
2445
- disposers = new _();
2056
+ disposers = new D();
2057
+ /** Array-level validation errors (e.g., "минимум 1 элемент") */
2058
+ _arrayErrors = g([]);
2446
2059
  // ============================================================================
2447
2060
  // Приватные поля для сохранения схем
2448
2061
  // ============================================================================
@@ -2464,15 +2077,15 @@ class F extends A {
2464
2077
  // Конструктор
2465
2078
  // ============================================================================
2466
2079
  constructor(e, t = []) {
2467
- super(), this.itemSchema = e, this.initialItems = t, this.items = d([]);
2080
+ super(), this.itemSchema = e, this.initialItems = t, this.items = g([]);
2468
2081
  for (const i of t)
2469
2082
  this.push(i);
2470
- this.length = l(() => this.items.value.length), this.value = l(() => this.items.value.map((i) => i.value.value)), this.valid = l(() => this.items.value.every((i) => i.valid.value)), this.invalid = l(() => !this.valid.value), this.pending = l(() => this.items.value.some((i) => i.pending.value)), this.touched = l(() => this.items.value.some((i) => i.touched.value)), this.dirty = l(() => this.items.value.some((i) => i.dirty.value)), this.errors = l(() => {
2471
- const i = [];
2472
- return this.items.value.forEach((s) => {
2473
- i.push(...s.errors.value);
2474
- }), i;
2475
- }), this.status = l(() => this.pending.value ? "pending" : this.invalid.value ? "invalid" : "valid");
2083
+ this.length = d(() => this.items.value.length), this.value = d(() => this.items.value.map((i) => i.value.value));
2084
+ const s = W({
2085
+ getChildren: () => this.items.value,
2086
+ ownErrors: this._arrayErrors
2087
+ });
2088
+ this.valid = s.valid, this.invalid = s.invalid, this.pending = s.pending, this.touched = s.touched, this.dirty = s.dirty, this.errors = s.errors, this.status = s.status;
2476
2089
  }
2477
2090
  // ============================================================================
2478
2091
  // CRUD операции
@@ -2488,9 +2101,23 @@ class F extends A {
2488
2101
  /**
2489
2102
  * Удалить элемент по индексу
2490
2103
  * @param index - Индекс элемента для удаления
2104
+ *
2105
+ * @remarks
2106
+ * Вызывает dispose() на удаляемом элементе для очистки подписок
2491
2107
  */
2492
2108
  removeAt(e) {
2493
- e < 0 || e >= this.items.value.length || (this.items.value = this.items.value.filter((t, i) => i !== e));
2109
+ if (e < 0 || e >= this.items.value.length) {
2110
+ S.handle(
2111
+ new Error(
2112
+ `ArrayNode.removeAt: index ${e} out of bounds (length: ${this.items.value.length})`
2113
+ ),
2114
+ "ArrayNode.removeAt",
2115
+ V.LOG
2116
+ );
2117
+ return;
2118
+ }
2119
+ const t = this.items.value[e];
2120
+ this.items.value = this.items.value.filter((s, i) => i !== e), t && "dispose" in t && typeof t.dispose == "function" && t.dispose();
2494
2121
  }
2495
2122
  /**
2496
2123
  * Вставить элемент в массив
@@ -2498,24 +2125,40 @@ class F extends A {
2498
2125
  * @param initialValue - Начальные значения для нового элемента
2499
2126
  */
2500
2127
  insert(e, t) {
2501
- if (e < 0 || e > this.items.value.length)
2128
+ if (e < 0 || e > this.items.value.length) {
2129
+ S.handle(
2130
+ new Error(
2131
+ `ArrayNode.insert: index ${e} out of bounds (length: ${this.items.value.length})`
2132
+ ),
2133
+ "ArrayNode.insert",
2134
+ V.LOG
2135
+ );
2502
2136
  return;
2503
- const i = this.createItem(t), s = [...this.items.value];
2504
- s.splice(e, 0, i), this.items.value = s;
2137
+ }
2138
+ const s = this.createItem(t), i = [...this.items.value];
2139
+ i.splice(e, 0, s), this.items.value = i;
2505
2140
  }
2506
2141
  /**
2507
2142
  * Удалить все элементы массива
2143
+ *
2144
+ * @remarks
2145
+ * Вызывает dispose() на всех элементах для очистки подписок
2508
2146
  */
2509
2147
  clear() {
2510
- this.items.value = [];
2148
+ const e = [...this.items.value];
2149
+ this.items.value = [], e.forEach((t) => {
2150
+ "dispose" in t && typeof t.dispose == "function" && t.dispose();
2151
+ });
2511
2152
  }
2512
2153
  /**
2513
2154
  * Получить элемент по индексу
2514
2155
  * @param index - Индекс элемента
2515
- * @returns Типизированный GroupNode или undefined если индекс вне границ
2156
+ * @returns Типизированный GroupNode proxy или undefined если индекс вне границ
2516
2157
  */
2517
2158
  at(e) {
2518
- return this.items.value[e];
2159
+ const t = this.items.value[e];
2160
+ if (t)
2161
+ return t.getProxy();
2519
2162
  }
2520
2163
  // ============================================================================
2521
2164
  // Реализация абстрактных методов
@@ -2524,12 +2167,13 @@ class F extends A {
2524
2167
  return this.items.value.map((e) => e.getValue());
2525
2168
  }
2526
2169
  setValue(e, t) {
2527
- this.clear(), e.forEach((i) => this.push(i)), t?.emitEvent !== !1 && this.validate().catch(() => {
2170
+ this.clear(), e.forEach((s) => this.push(s)), t?.emitEvent !== !1 && this.validate().catch((s) => {
2171
+ S.handle(s, "ArrayNode.setValue", V.LOG);
2528
2172
  });
2529
2173
  }
2530
2174
  patchValue(e) {
2531
- e.forEach((t, i) => {
2532
- this.items.value[i] && t !== void 0 && this.items.value[i].patchValue(t);
2175
+ e.forEach((t, s) => {
2176
+ this.items.value[s] && t !== void 0 && this.items.value[s].patchValue(t);
2533
2177
  });
2534
2178
  }
2535
2179
  /**
@@ -2550,7 +2194,7 @@ class F extends A {
2550
2194
  * ```
2551
2195
  */
2552
2196
  reset(e) {
2553
- this.clear(), e && e.forEach((t) => this.push(t));
2197
+ this._arrayErrors.value = [], this.clear(), e && e.forEach((t) => this.push(t));
2554
2198
  }
2555
2199
  /**
2556
2200
  * Сбросить массив к исходным значениям (initialItems)
@@ -2581,15 +2225,38 @@ class F extends A {
2581
2225
  * ```
2582
2226
  */
2583
2227
  resetToInitial() {
2584
- this.clear(), this.initialItems.forEach((e) => this.push(e));
2228
+ this._arrayErrors.value = [], this.clear(), this.initialItems.forEach((e) => this.push(e));
2585
2229
  }
2586
2230
  async validate() {
2587
2231
  return (await Promise.all(this.items.value.map((t) => t.validate()))).every(Boolean);
2588
2232
  }
2233
+ /**
2234
+ * Установить array-level validation errors
2235
+ *
2236
+ * @param errors - Массив ошибок валидации уровня массива
2237
+ *
2238
+ * @example
2239
+ * ```typescript
2240
+ * arrayNode.setErrors([{
2241
+ * code: 'minItems',
2242
+ * message: 'Минимум 1 элемент обязателен',
2243
+ * }]);
2244
+ * ```
2245
+ */
2589
2246
  setErrors(e) {
2247
+ this._arrayErrors.value = e;
2590
2248
  }
2249
+ /**
2250
+ * Очистить все errors (array-level + item-level)
2251
+ *
2252
+ * @example
2253
+ * ```typescript
2254
+ * arrayNode.clearErrors();
2255
+ * console.log(arrayNode.errors.value); // []
2256
+ * ```
2257
+ */
2591
2258
  clearErrors() {
2592
- this.items.value.forEach((e) => e.clearErrors());
2259
+ this._arrayErrors.value = [], this.items.value.forEach((e) => e.clearErrors());
2593
2260
  }
2594
2261
  // ============================================================================
2595
2262
  // Protected hooks (Template Method pattern)
@@ -2631,20 +2298,24 @@ class F extends A {
2631
2298
  // ============================================================================
2632
2299
  /**
2633
2300
  * Итерировать по элементам массива
2634
- * @param callback - Функция, вызываемая для каждого элемента с типизированным GroupNode
2301
+ * @param callback - Функция, вызываемая для каждого элемента с типизированным GroupNode proxy
2635
2302
  */
2636
2303
  forEach(e) {
2637
- this.items.value.forEach((t, i) => {
2638
- e(t, i);
2304
+ this.items.value.forEach((t, s) => {
2305
+ const i = t.getProxy();
2306
+ e(i, s);
2639
2307
  });
2640
2308
  }
2641
2309
  /**
2642
2310
  * Маппинг элементов массива
2643
- * @param callback - Функция преобразования с типизированным GroupNode
2311
+ * @param callback - Функция преобразования с типизированным GroupNode proxy
2644
2312
  * @returns Новый массив результатов
2645
2313
  */
2646
2314
  map(e) {
2647
- return this.items.value.map((t, i) => e(t, i));
2315
+ return this.items.value.map((t, s) => {
2316
+ const i = t.getProxy();
2317
+ return e(i, s);
2318
+ });
2648
2319
  }
2649
2320
  // ============================================================================
2650
2321
  // Private методы
@@ -2655,7 +2326,7 @@ class F extends A {
2655
2326
  */
2656
2327
  createItem(e) {
2657
2328
  if (this.isGroupSchema(this.itemSchema)) {
2658
- const t = new m(this.itemSchema);
2329
+ const t = new k(this.itemSchema);
2659
2330
  return e && t.patchValue(e), this.validationSchemaFn && "applyValidationSchema" in t && t.applyValidationSchema(this.validationSchemaFn), this.behaviorSchemaFn && "applyBehaviorSchema" in t && t.applyBehaviorSchema(this.behaviorSchemaFn), t;
2660
2331
  }
2661
2332
  throw new Error(
@@ -2742,14 +2413,14 @@ class F extends A {
2742
2413
  * ```
2743
2414
  */
2744
2415
  watchItems(e, t) {
2745
- const i = v(() => {
2746
- const a = this.items.value.map((n) => {
2747
- if (n instanceof m)
2416
+ const s = m(() => {
2417
+ const r = this.items.value.map((n) => {
2418
+ if (n instanceof k)
2748
2419
  return n.getFieldByPath(e)?.value.value;
2749
2420
  });
2750
- t(a);
2751
- }), s = `watchItems-${Date.now()}-${Math.random()}`;
2752
- return this.disposers.add(s, i);
2421
+ t(r);
2422
+ }), i = A(w.WatchItems);
2423
+ return this.disposers.add(i, s);
2753
2424
  }
2754
2425
  /**
2755
2426
  * Подписка на изменение длины массива
@@ -2774,11 +2445,11 @@ class F extends A {
2774
2445
  * ```
2775
2446
  */
2776
2447
  watchLength(e) {
2777
- const t = v(() => {
2778
- const s = this.length.value;
2779
- e(s);
2780
- }), i = `watchLength-${Date.now()}-${Math.random()}`;
2781
- return this.disposers.add(i, t);
2448
+ const t = m(() => {
2449
+ const i = this.length.value;
2450
+ e(i);
2451
+ }), s = A(w.WatchLength);
2452
+ return this.disposers.add(s, t);
2782
2453
  }
2783
2454
  /**
2784
2455
  * Очистить все ресурсы узла
@@ -2841,7 +2512,7 @@ class F extends A {
2841
2512
  });
2842
2513
  }
2843
2514
  }
2844
- class L {
2515
+ class ne {
2845
2516
  /**
2846
2517
  * Создает узел формы на основе конфигурации
2847
2518
  *
@@ -2891,16 +2562,16 @@ class L {
2891
2562
  if (Array.isArray(e) && e.length >= 1)
2892
2563
  return this.createArrayNodeFromArray(e);
2893
2564
  if (this.isFieldConfig(e))
2894
- return new I(e);
2565
+ return new ee(e);
2895
2566
  if (this.isArrayConfig(e)) {
2896
2567
  const t = e;
2897
- return new F(
2568
+ return new L(
2898
2569
  t.schema,
2899
2570
  t.initialItems
2900
2571
  );
2901
2572
  }
2902
2573
  if (this.isGroupConfig(e))
2903
- return new m(e);
2574
+ return new k(e);
2904
2575
  throw new Error(
2905
2576
  `NodeFactory: Unknown node config. Expected FieldConfig, GroupConfig, or ArrayConfig, but got: ${JSON.stringify(
2906
2577
  e
@@ -2934,11 +2605,11 @@ class L {
2934
2605
  */
2935
2606
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
2936
2607
  createArrayNodeFromArray(e) {
2937
- const [t, ...i] = e, s = [];
2938
- this.isGroupConfig(t) && s.push(this.extractValues(t));
2939
- for (const a of i)
2940
- this.isGroupConfig(a) ? s.push(this.extractValues(a)) : s.push(a);
2941
- return new F(t, s);
2608
+ const [t, ...s] = e, i = [];
2609
+ this.isGroupConfig(t) && i.push(this.extractValues(t));
2610
+ for (const r of s)
2611
+ this.isGroupConfig(r) ? i.push(this.extractValues(r)) : i.push(r);
2612
+ return new L(t, i);
2942
2613
  }
2943
2614
  /**
2944
2615
  * Извлечь значения из схемы (рекурсивно)
@@ -2976,8 +2647,8 @@ class L {
2976
2647
  return e.map((t) => this.extractValues(t));
2977
2648
  if (this.isGroupConfig(e)) {
2978
2649
  const t = {};
2979
- for (const [i, s] of Object.entries(e))
2980
- t[i] = this.extractValues(s);
2650
+ for (const [s, i] of Object.entries(e))
2651
+ t[s] = this.extractValues(i);
2981
2652
  return t;
2982
2653
  }
2983
2654
  return e;
@@ -3057,287 +2728,441 @@ class L {
3057
2728
  return e != null && typeof e == "object" && !this.isFieldConfig(e) && !this.isArrayConfig(e);
3058
2729
  }
3059
2730
  }
3060
- class ee {
2731
+ function ge(a) {
2732
+ return new k(a).getProxy();
2733
+ }
2734
+ class be {
3061
2735
  /**
3062
- * Создать Debouncer с заданной задержкой
2736
+ * @param form - Форма для наблюдения
2737
+ * @param options - Опции observer
2738
+ */
2739
+ constructor(e, t) {
2740
+ this.form = e, this.options = {
2741
+ enableLogging: !1,
2742
+ eventTypes: ["value", "status", "errors", "touched", "dirty"],
2743
+ pathFilter: [],
2744
+ ...t
2745
+ };
2746
+ }
2747
+ listeners = /* @__PURE__ */ new Set();
2748
+ disposers = [];
2749
+ options;
2750
+ /**
2751
+ * Подписаться на события изменения
3063
2752
  *
3064
- * @param delay Задержка в миллисекундах (0 = без задержки)
2753
+ * @param callback - Функция обработки события
2754
+ * @returns Функция отписки
3065
2755
  *
3066
2756
  * @example
3067
2757
  * ```typescript
3068
- * const debouncer = new Debouncer(300); // 300мс задержка
3069
- * const immediate = new Debouncer(0); // Без задержки
2758
+ * const unsubscribe = observer.subscribe((event) => {
2759
+ * // Отправить событие в analytics
2760
+ * analytics.track('form_change', event);
2761
+ * });
3070
2762
  * ```
3071
2763
  */
3072
- constructor(e) {
3073
- this.delay = e;
2764
+ subscribe(e) {
2765
+ return this.listeners.add(e), () => this.listeners.delete(e);
3074
2766
  }
3075
2767
  /**
3076
- * Таймер для отложенного выполнения
3077
- * @private
3078
- */
3079
- timer;
3080
- /**
3081
- * Отложить выполнение функции
2768
+ * Включить трассировку формы
3082
2769
  *
3083
- * Если вызывается повторно до истечения delay, предыдущий вызов отменяется
3084
- * и таймер перезапускается.
2770
+ * Подписывается на изменения основных сигналов формы
2771
+ * и вызывает listeners при каждом изменении
3085
2772
  *
3086
- * @param fn Функция для выполнения (может быть async)
3087
- * @returns Promise, который разрешается результатом функции
2773
+ * @returns Функция для отключения трассировки
3088
2774
  *
3089
2775
  * @example
3090
2776
  * ```typescript
3091
- * const debouncer = new Debouncer(300);
3092
- *
3093
- * // Первый вызов - запланирован через 300мс
3094
- * debouncer.debounce(async () => console.log('First'));
3095
- *
3096
- * // Второй вызов через 100мс - отменяет первый, запланирован через 300мс
3097
- * debouncer.debounce(async () => console.log('Second'));
2777
+ * const dispose = observer.enableTracing();
3098
2778
  *
3099
- * // Через 300мс выведет только: "Second"
2779
+ * // Позже, для отключения
2780
+ * dispose();
3100
2781
  * ```
3101
2782
  */
3102
- async debounce(e) {
3103
- return new Promise((t, i) => {
3104
- if (this.timer && clearTimeout(this.timer), this.delay === 0) {
3105
- Promise.resolve().then(() => e()).then(t).catch(i);
3106
- return;
3107
- }
3108
- this.timer = setTimeout(async () => {
3109
- try {
3110
- const s = await e();
3111
- t(s);
3112
- } catch (s) {
3113
- i(s);
3114
- }
3115
- }, this.delay);
3116
- });
2783
+ enableTracing() {
2784
+ if (this.shouldTrack("value")) {
2785
+ let e = this.form.value.value;
2786
+ const t = m(() => {
2787
+ const s = this.form.value.value;
2788
+ s !== e && (this.emit({
2789
+ type: "value",
2790
+ path: "",
2791
+ timestamp: Date.now(),
2792
+ oldValue: e,
2793
+ newValue: s
2794
+ }), e = s);
2795
+ });
2796
+ this.disposers.push(t);
2797
+ }
2798
+ if (this.shouldTrack("status")) {
2799
+ let e = this.form.status.value;
2800
+ const t = m(() => {
2801
+ const s = this.form.status.value;
2802
+ s !== e && (this.emit({
2803
+ type: "status",
2804
+ path: "",
2805
+ timestamp: Date.now(),
2806
+ oldValue: e,
2807
+ newValue: s
2808
+ }), e = s);
2809
+ });
2810
+ this.disposers.push(t);
2811
+ }
2812
+ if (this.shouldTrack("errors")) {
2813
+ let e = this.form.errors.value;
2814
+ const t = m(() => {
2815
+ const s = this.form.errors.value;
2816
+ s !== e && (this.emit({
2817
+ type: "errors",
2818
+ path: "",
2819
+ timestamp: Date.now(),
2820
+ oldValue: e,
2821
+ newValue: s
2822
+ }), e = s);
2823
+ });
2824
+ this.disposers.push(t);
2825
+ }
2826
+ if (this.shouldTrack("touched")) {
2827
+ let e = this.form.touched.value;
2828
+ const t = m(() => {
2829
+ const s = this.form.touched.value;
2830
+ s !== e && (this.emit({
2831
+ type: "touched",
2832
+ path: "",
2833
+ timestamp: Date.now(),
2834
+ oldValue: e,
2835
+ newValue: s
2836
+ }), e = s);
2837
+ });
2838
+ this.disposers.push(t);
2839
+ }
2840
+ if (this.shouldTrack("dirty")) {
2841
+ let e = this.form.dirty.value;
2842
+ const t = m(() => {
2843
+ const s = this.form.dirty.value;
2844
+ s !== e && (this.emit({
2845
+ type: "dirty",
2846
+ path: "",
2847
+ timestamp: Date.now(),
2848
+ oldValue: e,
2849
+ newValue: s
2850
+ }), e = s);
2851
+ });
2852
+ this.disposers.push(t);
2853
+ }
2854
+ return () => {
2855
+ this.disposers.forEach((e) => e()), this.disposers = [];
2856
+ };
3117
2857
  }
3118
2858
  /**
3119
- * Отменить отложенное выполнение
2859
+ * Наблюдать за конкретным полем
3120
2860
  *
3121
- * Если есть запланированный вызов, он будет отменен и не выполнится.
3122
- * Promise из debounce() никогда не разрешится.
2861
+ * @param path - Путь к полю
2862
+ * @returns Функция для отключения наблюдения
3123
2863
  *
3124
2864
  * @example
3125
2865
  * ```typescript
3126
- * const debouncer = new Debouncer(300);
3127
- *
3128
- * debouncer.debounce(async () => {
3129
- * console.log('This will not execute');
3130
- * });
3131
- *
3132
- * debouncer.cancel(); // Отменяем вызов
2866
+ * // Наблюдать за полем email
2867
+ * const dispose = observer.watchField('email');
3133
2868
  * ```
3134
2869
  */
3135
- cancel() {
3136
- this.timer && (clearTimeout(this.timer), this.timer = void 0);
2870
+ watchField(e) {
2871
+ const t = this.form.getFieldByPath(e);
2872
+ if (!t)
2873
+ return () => {
2874
+ };
2875
+ const s = [];
2876
+ if (this.shouldTrack("value")) {
2877
+ let i = t.value.value;
2878
+ const r = m(() => {
2879
+ const n = t.value.value;
2880
+ n !== i && (this.emit({
2881
+ type: "value",
2882
+ path: e,
2883
+ timestamp: Date.now(),
2884
+ oldValue: i,
2885
+ newValue: n
2886
+ }), i = n);
2887
+ });
2888
+ s.push(r);
2889
+ }
2890
+ if (this.shouldTrack("status")) {
2891
+ let i = t.status.value;
2892
+ const r = m(() => {
2893
+ const n = t.status.value;
2894
+ n !== i && (this.emit({
2895
+ type: "status",
2896
+ path: e,
2897
+ timestamp: Date.now(),
2898
+ oldValue: i,
2899
+ newValue: n
2900
+ }), i = n);
2901
+ });
2902
+ s.push(r);
2903
+ }
2904
+ return () => s.forEach((i) => i());
3137
2905
  }
3138
2906
  /**
3139
- * Выполнить функцию немедленно, отменив любой отложенный вызов
3140
- *
3141
- * Полезно когда нужно принудительно выполнить действие сейчас,
3142
- * игнорируя debounce.
3143
- *
3144
- * @param fn Функция для немедленного выполнения
3145
- * @returns Promise с результатом функции
3146
- *
3147
- * @example
3148
- * ```typescript
3149
- * const debouncer = new Debouncer(300);
3150
- *
3151
- * // Запланировано через 300мс
3152
- * debouncer.debounce(async () => console.log('Delayed'));
3153
- *
3154
- * // Отменяем отложенный и выполняем немедленно
3155
- * await debouncer.flush(async () => console.log('Immediate'));
3156
- * // Выведет: "Immediate" (сразу)
3157
- * // "Delayed" не выполнится (отменен)
3158
- * ```
2907
+ * Отправить событие всем listeners
3159
2908
  */
3160
- async flush(e) {
3161
- return this.cancel(), await e();
2909
+ emit(e) {
2910
+ this.matchesPathFilter(e.path) && (this.options.enableLogging && console.log(`[FormObserver] ${e.type} at "${e.path || "root"}":`, e.newValue), this.listeners.forEach((t) => {
2911
+ try {
2912
+ t(e);
2913
+ } catch {
2914
+ }
2915
+ }));
3162
2916
  }
3163
2917
  /**
3164
- * Проверить, есть ли активный (запланированный) вызов
3165
- *
3166
- * @returns true если есть запланированный вызов
3167
- *
3168
- * @example
3169
- * ```typescript
3170
- * const debouncer = new Debouncer(300);
3171
- *
3172
- * console.log(debouncer.isPending()); // false
3173
- *
3174
- * debouncer.debounce(() => console.log('Test'));
3175
- * console.log(debouncer.isPending()); // true
3176
- *
3177
- * // Через 300мс
3178
- * console.log(debouncer.isPending()); // false
3179
- * ```
2918
+ * Проверить, нужно ли отслеживать тип события
3180
2919
  */
3181
- isPending() {
3182
- return this.timer !== void 0;
2920
+ shouldTrack(e) {
2921
+ return this.options.eventTypes.includes(e);
2922
+ }
2923
+ /**
2924
+ * Проверить, соответствует ли путь фильтру
2925
+ */
2926
+ matchesPathFilter(e) {
2927
+ const { pathFilter: t } = this.options;
2928
+ return !t || Array.isArray(t) && t.length === 0 ? !0 : t instanceof RegExp ? t.test(e) : t.includes(e);
2929
+ }
2930
+ /**
2931
+ * Очистить все подписки и disposers
2932
+ */
2933
+ dispose() {
2934
+ this.disposers.forEach((e) => e()), this.disposers = [], this.listeners.clear();
3183
2935
  }
3184
2936
  }
3185
- function te(r) {
3186
- return new m(r);
3187
- }
3188
- function j(r) {
3189
- return {
3190
- type: "static",
3191
- load: async () => ({
3192
- items: r,
3193
- totalCount: r.length
3194
- })
3195
- };
2937
+ var x = { exports: {} }, T = {};
2938
+ var B;
2939
+ function oe() {
2940
+ if (B) return T;
2941
+ B = 1;
2942
+ var a = U;
2943
+ function e(h, f) {
2944
+ return h === f && (h !== 0 || 1 / h === 1 / f) || h !== h && f !== f;
2945
+ }
2946
+ var t = typeof Object.is == "function" ? Object.is : e, s = a.useState, i = a.useEffect, r = a.useLayoutEffect, n = a.useDebugValue;
2947
+ function o(h, f) {
2948
+ var c = f(), p = s({ inst: { value: c, getSnapshot: f } }), y = p[0].inst, E = p[1];
2949
+ return r(
2950
+ function() {
2951
+ y.value = c, y.getSnapshot = f, l(y) && E({ inst: y });
2952
+ },
2953
+ [h, c, f]
2954
+ ), i(
2955
+ function() {
2956
+ return l(y) && E({ inst: y }), h(function() {
2957
+ l(y) && E({ inst: y });
2958
+ });
2959
+ },
2960
+ [h]
2961
+ ), n(c), c;
2962
+ }
2963
+ function l(h) {
2964
+ var f = h.getSnapshot;
2965
+ h = h.value;
2966
+ try {
2967
+ var c = f();
2968
+ return !t(h, c);
2969
+ } catch {
2970
+ return !0;
2971
+ }
2972
+ }
2973
+ function u(h, f) {
2974
+ return f();
2975
+ }
2976
+ var v = typeof window > "u" || typeof window.document > "u" || typeof window.document.createElement > "u" ? u : o;
2977
+ return T.useSyncExternalStore = a.useSyncExternalStore !== void 0 ? a.useSyncExternalStore : v, T;
3196
2978
  }
3197
- function K(r) {
3198
- let e = null;
3199
- return {
3200
- type: "preload",
3201
- load: async (t) => {
3202
- if (!e) {
3203
- const i = await r(t);
3204
- e = {
3205
- items: i,
3206
- totalCount: i.length
3207
- };
2979
+ var O = {};
2980
+ var G;
2981
+ function le() {
2982
+ return G || (G = 1, process.env.NODE_ENV !== "production" && (function() {
2983
+ function a(c, p) {
2984
+ return c === p && (c !== 0 || 1 / c === 1 / p) || c !== c && p !== p;
2985
+ }
2986
+ function e(c, p) {
2987
+ v || i.startTransition === void 0 || (v = !0, console.error(
2988
+ "You are using an outdated, pre-release alpha of React 18 that does not support useSyncExternalStore. The use-sync-external-store shim will not work correctly. Upgrade to a newer pre-release."
2989
+ ));
2990
+ var y = p();
2991
+ if (!h) {
2992
+ var E = p();
2993
+ r(y, E) || (console.error(
2994
+ "The result of getSnapshot should be cached to avoid an infinite loop"
2995
+ ), h = !0);
3208
2996
  }
3209
- return e;
2997
+ E = n({
2998
+ inst: { value: y, getSnapshot: p }
2999
+ });
3000
+ var _ = E[0].inst, F = E[1];
3001
+ return l(
3002
+ function() {
3003
+ _.value = y, _.getSnapshot = p, t(_) && F({ inst: _ });
3004
+ },
3005
+ [c, y, p]
3006
+ ), o(
3007
+ function() {
3008
+ return t(_) && F({ inst: _ }), c(function() {
3009
+ t(_) && F({ inst: _ });
3010
+ });
3011
+ },
3012
+ [c]
3013
+ ), u(y), y;
3210
3014
  }
3211
- };
3212
- }
3213
- function z(r) {
3214
- return {
3215
- type: "partial",
3216
- load: async (e) => {
3217
- const t = await r(e);
3218
- return {
3219
- items: t,
3220
- totalCount: t.length
3221
- // Можно расширить для поддержки серверной пагинации
3222
- };
3015
+ function t(c) {
3016
+ var p = c.getSnapshot;
3017
+ c = c.value;
3018
+ try {
3019
+ var y = p();
3020
+ return !r(c, y);
3021
+ } catch {
3022
+ return !0;
3023
+ }
3223
3024
  }
3224
- };
3025
+ function s(c, p) {
3026
+ return p();
3027
+ }
3028
+ typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ < "u" && typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart == "function" && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());
3029
+ var i = U, r = typeof Object.is == "function" ? Object.is : a, n = i.useState, o = i.useEffect, l = i.useLayoutEffect, u = i.useDebugValue, v = !1, h = !1, f = typeof window > "u" || typeof window.document > "u" || typeof window.document.createElement > "u" ? s : e;
3030
+ O.useSyncExternalStore = i.useSyncExternalStore !== void 0 ? i.useSyncExternalStore : f, typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ < "u" && typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop == "function" && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error());
3031
+ })()), O;
3225
3032
  }
3226
- const se = {
3227
- static: j,
3228
- preload: K,
3229
- partial: z
3230
- };
3231
- function k(r, e) {
3232
- if (r === e) return !0;
3233
- if (r.length !== e.length) return !1;
3234
- for (let t = 0; t < r.length; t++)
3235
- if (r[t] !== e[t]) return !1;
3033
+ var $;
3034
+ function ue() {
3035
+ return $ || ($ = 1, process.env.NODE_ENV === "production" ? x.exports = oe() : x.exports = le()), x.exports;
3036
+ }
3037
+ var R = ue();
3038
+ function he(a, e) {
3039
+ if (a === e) return !0;
3040
+ if (a.length !== e.length) return !1;
3041
+ for (let t = 0; t < a.length; t++)
3042
+ if (a[t] !== e[t]) return !1;
3236
3043
  return !0;
3237
3044
  }
3238
- function W(r) {
3239
- const e = V({
3240
- snapshot: null,
3241
- value: r.value.value,
3242
- errors: r.errors.value,
3243
- componentProps: r.componentProps.value,
3244
- disabled: r.disabled.value,
3245
- pending: r.pending.value,
3246
- valid: r.valid.value,
3247
- invalid: r.invalid.value,
3248
- touched: r.touched.value,
3249
- shouldShowError: r.shouldShowError.value
3250
- }), t = y(
3251
- (s) => {
3252
- let a = !0;
3253
- return v(() => {
3254
- if (r.value.value, r.disabled.value, r.errors.value, r.pending.value, r.valid.value, r.invalid.value, r.touched.value, r.shouldShowError.value, r.componentProps.value, a) {
3255
- a = !1;
3045
+ function q(a, e, t) {
3046
+ const s = P(null);
3047
+ if (s.current === null) {
3048
+ const n = {};
3049
+ for (const o in a)
3050
+ n[o] = a[o].value;
3051
+ s.current = { ...n, __snapshot: null };
3052
+ }
3053
+ const i = b(
3054
+ (n) => {
3055
+ let o = !0;
3056
+ return m(() => {
3057
+ for (const u in a)
3058
+ a[u].value;
3059
+ if (o) {
3060
+ o = !1;
3256
3061
  return;
3257
3062
  }
3258
- s();
3063
+ n();
3259
3064
  });
3260
3065
  },
3261
- [r]
3262
- ), i = y(() => {
3263
- const s = e.current, a = r.value.value, n = r.errors.value, o = r.componentProps.value, u = r.disabled.value, h = r.pending.value, c = r.valid.value, f = r.invalid.value, p = r.touched.value, b = r.shouldShowError.value;
3264
- return !(s.value !== a || !k(s.errors, n) || s.componentProps !== o || s.disabled !== u || s.pending !== h || s.valid !== c || s.invalid !== f || s.touched !== p || s.shouldShowError !== b) && s.snapshot || (s.value = a, s.errors = n, s.componentProps = o, s.disabled = u, s.pending = h, s.valid = c, s.invalid = f, s.touched = p, s.shouldShowError = b, s.snapshot = {
3265
- value: a,
3266
- pending: h,
3267
- disabled: u,
3268
- errors: n,
3269
- valid: c,
3270
- invalid: f,
3271
- touched: p,
3272
- shouldShowError: b,
3273
- componentProps: o
3274
- }), s.snapshot;
3275
- }, [r]);
3276
- return w(t, i, i);
3277
- }
3278
- function H(r) {
3279
- const e = V({
3280
- snapshot: null,
3281
- value: r.value.value,
3282
- length: r.length.value,
3283
- errors: r.errors.value,
3284
- pending: r.pending.value,
3285
- valid: r.valid.value,
3286
- invalid: r.invalid.value,
3287
- touched: r.touched.value,
3288
- dirty: r.dirty.value
3289
- }), t = y(
3290
- (s) => {
3291
- let a = !0;
3292
- return v(() => {
3293
- if (r.value.value, r.length.value, r.errors.value, r.pending.value, r.valid.value, r.invalid.value, r.touched.value, r.dirty.value, a) {
3294
- a = !1;
3295
- return;
3066
+ [a]
3067
+ ), r = b(() => {
3068
+ const n = s.current, o = {};
3069
+ for (const u in a)
3070
+ o[u] = a[u].value;
3071
+ let l = !1;
3072
+ for (const u of e) {
3073
+ const { key: v, useShallowArrayEqual: h } = u, f = o[v], c = n[v];
3074
+ if (h) {
3075
+ if (!he(c, f)) {
3076
+ l = !0;
3077
+ break;
3296
3078
  }
3297
- s();
3298
- });
3299
- },
3300
- [r]
3301
- ), i = y(() => {
3302
- const s = e.current, a = r.value.value, n = r.length.value, o = r.errors.value, u = r.pending.value, h = r.valid.value, c = r.invalid.value, f = r.touched.value, p = r.dirty.value;
3303
- return !(s.value !== a || s.length !== n || !k(s.errors, o) || s.pending !== u || s.valid !== h || s.invalid !== c || s.touched !== f || s.dirty !== p) && s.snapshot || (s.value = a, s.length = n, s.errors = o, s.pending = u, s.valid = h, s.invalid = c, s.touched = f, s.dirty = p, s.snapshot = {
3304
- value: a,
3305
- length: n,
3306
- pending: u,
3307
- errors: o,
3308
- valid: h,
3309
- invalid: c,
3310
- touched: f,
3311
- dirty: p
3312
- }), s.snapshot;
3313
- }, [r]);
3314
- return w(t, i, i);
3079
+ } else if (c !== f) {
3080
+ l = !0;
3081
+ break;
3082
+ }
3083
+ }
3084
+ if (!l && n.__snapshot)
3085
+ return n.__snapshot;
3086
+ for (const u in a)
3087
+ n[u] = o[u];
3088
+ return n.__snapshot = t(o), n.__snapshot;
3089
+ }, [a, t]);
3090
+ return R.useSyncExternalStore(i, r, r);
3315
3091
  }
3316
- function ie(r) {
3317
- const e = V({
3318
- value: r.value.value,
3319
- snapshot: r.value.value
3320
- }), t = y(
3321
- (s) => {
3322
- let a = !0;
3323
- return v(() => {
3324
- if (r.value.value, a) {
3325
- a = !1;
3326
- return;
3327
- }
3328
- s();
3329
- });
3330
- },
3331
- [r]
3332
- ), i = y(() => {
3333
- const s = r.value.value;
3334
- return e.current.value === s ? e.current.snapshot : (e.current.value = s, e.current.snapshot = s, s);
3335
- }, [r]);
3336
- return w(t, i, i);
3092
+ function de(a) {
3093
+ const e = {
3094
+ value: a.value,
3095
+ disabled: a.disabled,
3096
+ errors: a.errors,
3097
+ pending: a.pending,
3098
+ valid: a.valid,
3099
+ invalid: a.invalid,
3100
+ touched: a.touched,
3101
+ shouldShowError: a.shouldShowError,
3102
+ componentProps: a.componentProps
3103
+ }, t = [
3104
+ { key: "value" },
3105
+ { key: "disabled" },
3106
+ { key: "errors", useShallowArrayEqual: !0 },
3107
+ { key: "pending" },
3108
+ { key: "valid" },
3109
+ { key: "invalid" },
3110
+ { key: "touched" },
3111
+ { key: "shouldShowError" },
3112
+ { key: "componentProps" }
3113
+ ], s = b(
3114
+ (i) => ({
3115
+ value: i.value,
3116
+ pending: i.pending,
3117
+ disabled: i.disabled,
3118
+ errors: i.errors,
3119
+ valid: i.valid,
3120
+ invalid: i.invalid,
3121
+ touched: i.touched,
3122
+ shouldShowError: i.shouldShowError,
3123
+ componentProps: i.componentProps
3124
+ }),
3125
+ []
3126
+ );
3127
+ return q(e, t, s);
3128
+ }
3129
+ function ce(a) {
3130
+ const e = {
3131
+ value: a.value,
3132
+ length: a.length,
3133
+ errors: a.errors,
3134
+ pending: a.pending,
3135
+ valid: a.valid,
3136
+ invalid: a.invalid,
3137
+ touched: a.touched,
3138
+ dirty: a.dirty
3139
+ }, t = [
3140
+ { key: "value" },
3141
+ { key: "length" },
3142
+ { key: "errors", useShallowArrayEqual: !0 },
3143
+ { key: "pending" },
3144
+ { key: "valid" },
3145
+ { key: "invalid" },
3146
+ { key: "touched" },
3147
+ { key: "dirty" }
3148
+ ], s = b(
3149
+ (i) => ({
3150
+ value: i.value,
3151
+ length: i.length,
3152
+ pending: i.pending,
3153
+ errors: i.errors,
3154
+ valid: i.valid,
3155
+ invalid: i.invalid,
3156
+ touched: i.touched,
3157
+ dirty: i.dirty
3158
+ }),
3159
+ []
3160
+ );
3161
+ return q(e, t, s);
3337
3162
  }
3338
- function re(r) {
3339
- const e = r && "length" in r && "map" in r;
3340
- return r ? e ? H(r) : W(r) : {
3163
+ function Ee(a) {
3164
+ const e = a && "length" in a && "map" in a;
3165
+ return a ? e ? ce(a) : de(a) : {
3341
3166
  value: [],
3342
3167
  length: 0,
3343
3168
  pending: !1,
@@ -3348,33 +3173,99 @@ function re(r) {
3348
3173
  dirty: !1
3349
3174
  };
3350
3175
  }
3176
+ function _e(a) {
3177
+ const e = P({ value: a.value.value }), t = b(
3178
+ (i) => {
3179
+ let r = !0;
3180
+ return m(() => {
3181
+ if (a.value.value, r) {
3182
+ r = !1;
3183
+ return;
3184
+ }
3185
+ i();
3186
+ });
3187
+ },
3188
+ [a]
3189
+ ), s = b(() => {
3190
+ const i = a.value.value;
3191
+ return e.current.value === i ? e.current.value : (e.current.value = i, i);
3192
+ }, [a]);
3193
+ return R.useSyncExternalStore(t, s, s);
3194
+ }
3195
+ function Se(a) {
3196
+ const e = P({ length: a.length.value }), t = b(
3197
+ (i) => {
3198
+ let r = !0;
3199
+ return m(() => {
3200
+ if (a.length.value, r) {
3201
+ r = !1;
3202
+ return;
3203
+ }
3204
+ i();
3205
+ });
3206
+ },
3207
+ [a]
3208
+ ), s = b(() => {
3209
+ const i = a.length.value;
3210
+ return e.current.length === i ? e.current.length : (e.current.length = i, i);
3211
+ }, [a]);
3212
+ return R.useSyncExternalStore(t, s, s);
3213
+ }
3214
+ function Ve(a, e, t) {
3215
+ const s = b(
3216
+ (r) => {
3217
+ const n = e, o = [];
3218
+ for (const l of Object.keys(n)) {
3219
+ const u = n[l];
3220
+ if (u && typeof u == "object" && u.value && typeof u.value.subscribe == "function") {
3221
+ const v = u.value.subscribe(r);
3222
+ o.push(v);
3223
+ }
3224
+ }
3225
+ return () => {
3226
+ o.forEach((l) => l());
3227
+ };
3228
+ },
3229
+ [e]
3230
+ ), i = b(() => a ? a(e, t) : !1, [a, e, t]);
3231
+ return Q(s, i, i);
3232
+ }
3351
3233
  export {
3352
- F as ArrayNode,
3353
- ee as Debouncer,
3354
- g as ErrorStrategy,
3355
- I as FieldNode,
3356
- O as FieldPathNavigator,
3357
- E as FormErrorHandler,
3358
- A as FormNode,
3359
- m as GroupNode,
3360
- L as NodeFactory,
3361
- ye as RegistryStack,
3362
- se as Resources,
3363
- _ as SubscriptionManager,
3364
- oe as behaviors,
3365
- te as createForm,
3366
- me as getCurrentBehaviorRegistry,
3367
- ge as getCurrentValidationRegistry,
3368
- ue as getNodeType,
3369
- he as isArrayNode,
3370
- R as isFieldNode,
3371
- de as isFormNode,
3372
- ce as isGroupNode,
3373
- z as partialResource,
3374
- K as preloadResource,
3375
- j as staticResource,
3376
- re as useFormControl,
3377
- ie as useFormControlValue,
3378
- ve as validateForm,
3379
- fe as validators
3234
+ ke as AbstractRegistry,
3235
+ L as ArrayNode,
3236
+ V as ErrorStrategy,
3237
+ ee as FieldNode,
3238
+ se as FieldPathNavigator,
3239
+ S as FormErrorHandler,
3240
+ C as FormNode,
3241
+ be as FormObserver,
3242
+ Z as FormStatusMachine,
3243
+ ae as FormSubmitter,
3244
+ k as GroupNode,
3245
+ ne as NodeFactory,
3246
+ xe as RegistryStack,
3247
+ D as SubscriptionManager,
3248
+ $e as behaviors,
3249
+ I as createFieldPath,
3250
+ ge as createForm,
3251
+ Ie as extractKey,
3252
+ Le as extractPath,
3253
+ Fe as getCurrentBehaviorRegistry,
3254
+ Te as getCurrentValidationRegistry,
3255
+ Pe as getNodeType,
3256
+ N as isArrayNode,
3257
+ M as isFieldNode,
3258
+ Ce as isFormNode,
3259
+ De as isGroupNode,
3260
+ We as runOutsideEffect,
3261
+ qe as safeCallback,
3262
+ je as safeDebouncedCallback,
3263
+ Be as toFieldPath,
3264
+ A as uniqueId,
3265
+ Se as useArrayLength,
3266
+ Ee as useFormControl,
3267
+ _e as useFormControlValue,
3268
+ Ve as useHiddenCondition,
3269
+ Re as validateForm,
3270
+ Me as validators
3380
3271
  };