@reformer/core 1.1.0 → 2.0.0-beta.11

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 (221) 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.d.ts +6 -2
  18. package/dist/behaviors.js +27 -228
  19. package/dist/core/behavior/behavior-context.d.ts +32 -14
  20. package/dist/core/behavior/behavior-registry.d.ts +15 -27
  21. package/dist/core/behavior/behaviors/compute-from.d.ts +50 -21
  22. package/dist/core/behavior/behaviors/copy-from.d.ts +39 -14
  23. package/dist/core/behavior/behaviors/enable-when.d.ts +88 -19
  24. package/dist/core/behavior/behaviors/reset-when.d.ts +31 -18
  25. package/dist/core/behavior/behaviors/revalidate-when.d.ts +40 -17
  26. package/dist/core/behavior/behaviors/sync-fields.d.ts +34 -14
  27. package/dist/core/behavior/behaviors/transform-value.d.ts +116 -44
  28. package/dist/core/behavior/behaviors/watch-field.d.ts +66 -21
  29. package/dist/core/behavior/compose-behavior.d.ts +2 -12
  30. package/dist/core/behavior/index.d.ts +0 -1
  31. package/dist/core/behavior/types.d.ts +2 -8
  32. package/dist/core/factories/node-factory.d.ts +6 -29
  33. package/dist/core/nodes/array-node.d.ts +42 -22
  34. package/dist/core/nodes/field-node.d.ts +51 -26
  35. package/dist/core/nodes/form-node.d.ts +18 -20
  36. package/dist/core/nodes/group-node.d.ts +37 -212
  37. package/dist/core/types/deep-schema.d.ts +2 -12
  38. package/dist/core/types/field-path.d.ts +1 -1
  39. package/dist/core/types/form-context.d.ts +36 -30
  40. package/dist/core/types/{group-node-proxy.d.ts → form-proxy.d.ts} +12 -42
  41. package/dist/core/types/index.d.ts +52 -6
  42. package/dist/core/types/validation-schema.d.ts +3 -12
  43. package/dist/core/utils/abstract-registry.d.ts +74 -0
  44. package/dist/core/utils/aggregate-signals.d.ts +71 -0
  45. package/dist/core/utils/create-form.d.ts +3 -20
  46. package/dist/core/utils/error-handler.d.ts +1 -18
  47. package/dist/core/utils/field-path-navigator.d.ts +1 -1
  48. package/dist/core/{validation → utils}/field-path.d.ts +23 -6
  49. package/dist/core/utils/form-observer.d.ts +176 -0
  50. package/dist/core/utils/form-proxy-builder.d.ts +25 -0
  51. package/dist/core/utils/form-submitter.d.ts +121 -0
  52. package/dist/core/utils/index.d.ts +10 -2
  53. package/dist/core/utils/registry-helpers.d.ts +0 -7
  54. package/dist/core/utils/safe-effect.d.ts +73 -0
  55. package/dist/core/utils/status-machine.d.ts +153 -0
  56. package/dist/core/utils/type-guards.d.ts +5 -23
  57. package/dist/core/utils/unique-id.d.ts +53 -0
  58. package/dist/core/validation/core/apply-when.d.ts +3 -9
  59. package/dist/core/validation/core/apply.d.ts +2 -13
  60. package/dist/core/validation/core/validate-async.d.ts +2 -8
  61. package/dist/core/validation/core/validate-tree.d.ts +10 -10
  62. package/dist/core/validation/core/validate.d.ts +1 -7
  63. package/dist/core/validation/index.d.ts +8 -2
  64. package/dist/core/validation/validate-form.d.ts +1 -38
  65. package/dist/core/validation/validation-applicator.d.ts +2 -21
  66. package/dist/core/validation/validation-context.d.ts +67 -28
  67. package/dist/core/validation/validation-registry.d.ts +11 -25
  68. package/dist/core/validation/validators/array-validators.d.ts +2 -12
  69. package/dist/core/validation/validators/date-utils.d.ts +26 -0
  70. package/dist/core/validation/validators/email.d.ts +2 -9
  71. package/dist/core/validation/validators/future-date.d.ts +35 -0
  72. package/dist/core/validation/validators/index.d.ts +7 -1
  73. package/dist/core/validation/validators/is-date.d.ts +36 -0
  74. package/dist/core/validation/validators/max-age.d.ts +36 -0
  75. package/dist/core/validation/validators/max-date.d.ts +36 -0
  76. package/dist/core/validation/validators/max-length.d.ts +3 -10
  77. package/dist/core/validation/validators/max.d.ts +3 -10
  78. package/dist/core/validation/validators/min-age.d.ts +36 -0
  79. package/dist/core/validation/validators/min-date.d.ts +36 -0
  80. package/dist/core/validation/validators/min-length.d.ts +3 -10
  81. package/dist/core/validation/validators/min.d.ts +3 -10
  82. package/dist/core/validation/validators/number.d.ts +2 -9
  83. package/dist/core/validation/validators/past-date.d.ts +35 -0
  84. package/dist/core/validation/validators/pattern.d.ts +2 -9
  85. package/dist/core/validation/validators/phone.d.ts +2 -9
  86. package/dist/core/validation/validators/required.d.ts +2 -9
  87. package/dist/core/validation/validators/url.d.ts +2 -9
  88. package/dist/date-utils-xUWFslTj.js +29 -0
  89. package/dist/field-path-DuKdGcIE.js +66 -0
  90. package/dist/hooks/types.d.ts +328 -0
  91. package/dist/hooks/useArrayLength.d.ts +31 -0
  92. package/dist/hooks/useFormControl.d.ts +15 -39
  93. package/dist/hooks/useFormControlValue.d.ts +167 -0
  94. package/dist/hooks/useHiddenCondition.d.ts +25 -0
  95. package/dist/hooks/useSignalSubscription.d.ts +17 -0
  96. package/dist/index-D25LsbRm.js +73 -0
  97. package/dist/index.d.ts +8 -1
  98. package/dist/index.js +3271 -8
  99. package/dist/registry-helpers-Bv_BJ1s-.js +615 -0
  100. package/dist/safe-effect-Dh8uw81c.js +20 -0
  101. package/dist/validate-C3XiA_zf.js +10 -0
  102. package/dist/validators/email.d.ts +2 -0
  103. package/dist/validators/email.js +13 -0
  104. package/dist/validators/future-date.d.ts +2 -0
  105. package/dist/validators/future-date.js +20 -0
  106. package/dist/validators/is-date.d.ts +2 -0
  107. package/dist/validators/is-date.js +12 -0
  108. package/dist/validators/max-age.d.ts +2 -0
  109. package/dist/validators/max-age.js +20 -0
  110. package/dist/validators/max-date.d.ts +2 -0
  111. package/dist/validators/max-date.js +20 -0
  112. package/dist/validators/max-length.d.ts +2 -0
  113. package/dist/validators/max-length.js +11 -0
  114. package/dist/validators/max.d.ts +2 -0
  115. package/dist/validators/max.js +11 -0
  116. package/dist/validators/min-age.d.ts +2 -0
  117. package/dist/validators/min-age.js +20 -0
  118. package/dist/validators/min-date.d.ts +2 -0
  119. package/dist/validators/min-date.js +20 -0
  120. package/dist/validators/min-length.d.ts +2 -0
  121. package/dist/validators/min-length.js +11 -0
  122. package/dist/validators/min.d.ts +2 -0
  123. package/dist/validators/min.js +11 -0
  124. package/dist/validators/number.d.ts +2 -0
  125. package/dist/validators/number.js +35 -0
  126. package/dist/validators/past-date.d.ts +2 -0
  127. package/dist/validators/past-date.js +20 -0
  128. package/dist/validators/pattern.d.ts +2 -0
  129. package/dist/validators/pattern.js +11 -0
  130. package/dist/validators/phone.d.ts +2 -0
  131. package/dist/validators/phone.js +35 -0
  132. package/dist/validators/required.d.ts +2 -0
  133. package/dist/validators/required.js +15 -0
  134. package/dist/validators/url.d.ts +2 -0
  135. package/dist/validators/url.js +19 -0
  136. package/dist/validators-BGsNOgT1.js +207 -0
  137. package/dist/validators.d.ts +6 -2
  138. package/dist/validators.js +54 -296
  139. package/llms.txt +8887 -59
  140. package/package.json +87 -8
  141. package/dist/core/behavior/behavior-applicator.d.ts +0 -71
  142. package/dist/core/behavior/behavior-applicator.js +0 -92
  143. package/dist/core/behavior/behavior-context.js +0 -38
  144. package/dist/core/behavior/behavior-registry.js +0 -198
  145. package/dist/core/behavior/behaviors/compute-from.js +0 -84
  146. package/dist/core/behavior/behaviors/copy-from.js +0 -64
  147. package/dist/core/behavior/behaviors/enable-when.js +0 -81
  148. package/dist/core/behavior/behaviors/index.js +0 -11
  149. package/dist/core/behavior/behaviors/reset-when.js +0 -63
  150. package/dist/core/behavior/behaviors/revalidate-when.js +0 -51
  151. package/dist/core/behavior/behaviors/sync-fields.js +0 -66
  152. package/dist/core/behavior/behaviors/transform-value.js +0 -110
  153. package/dist/core/behavior/behaviors/watch-field.js +0 -56
  154. package/dist/core/behavior/compose-behavior.js +0 -166
  155. package/dist/core/behavior/create-field-path.d.ts +0 -20
  156. package/dist/core/behavior/create-field-path.js +0 -69
  157. package/dist/core/behavior/index.js +0 -17
  158. package/dist/core/behavior/types.js +0 -7
  159. package/dist/core/context/form-context-impl.d.ts +0 -29
  160. package/dist/core/context/form-context-impl.js +0 -37
  161. package/dist/core/factories/index.js +0 -6
  162. package/dist/core/factories/node-factory.js +0 -281
  163. package/dist/core/nodes/array-node.js +0 -534
  164. package/dist/core/nodes/field-node.js +0 -510
  165. package/dist/core/nodes/form-node.js +0 -343
  166. package/dist/core/nodes/group-node/field-registry.d.ts +0 -191
  167. package/dist/core/nodes/group-node/field-registry.js +0 -215
  168. package/dist/core/nodes/group-node/index.d.ts +0 -11
  169. package/dist/core/nodes/group-node/index.js +0 -11
  170. package/dist/core/nodes/group-node/proxy-builder.d.ts +0 -71
  171. package/dist/core/nodes/group-node/proxy-builder.js +0 -161
  172. package/dist/core/nodes/group-node/state-manager.d.ts +0 -184
  173. package/dist/core/nodes/group-node/state-manager.js +0 -265
  174. package/dist/core/nodes/group-node.js +0 -770
  175. package/dist/core/types/deep-schema.js +0 -11
  176. package/dist/core/types/field-path.js +0 -4
  177. package/dist/core/types/form-context.js +0 -25
  178. package/dist/core/types/group-node-proxy.js +0 -31
  179. package/dist/core/types/index.js +0 -4
  180. package/dist/core/types/validation-schema.js +0 -10
  181. package/dist/core/utils/create-form.js +0 -24
  182. package/dist/core/utils/debounce.d.ts +0 -160
  183. package/dist/core/utils/debounce.js +0 -197
  184. package/dist/core/utils/error-handler.js +0 -226
  185. package/dist/core/utils/field-path-navigator.js +0 -374
  186. package/dist/core/utils/index.js +0 -14
  187. package/dist/core/utils/registry-helpers.js +0 -79
  188. package/dist/core/utils/registry-stack.js +0 -86
  189. package/dist/core/utils/resources.d.ts +0 -41
  190. package/dist/core/utils/resources.js +0 -69
  191. package/dist/core/utils/subscription-manager.js +0 -214
  192. package/dist/core/utils/type-guards.js +0 -169
  193. package/dist/core/validation/core/apply-when.js +0 -41
  194. package/dist/core/validation/core/apply.js +0 -38
  195. package/dist/core/validation/core/index.js +0 -8
  196. package/dist/core/validation/core/validate-async.js +0 -45
  197. package/dist/core/validation/core/validate-tree.js +0 -37
  198. package/dist/core/validation/core/validate.js +0 -38
  199. package/dist/core/validation/field-path.js +0 -147
  200. package/dist/core/validation/index.js +0 -33
  201. package/dist/core/validation/validate-form.js +0 -152
  202. package/dist/core/validation/validation-applicator.js +0 -217
  203. package/dist/core/validation/validation-context.js +0 -75
  204. package/dist/core/validation/validation-registry.js +0 -298
  205. package/dist/core/validation/validators/array-validators.js +0 -86
  206. package/dist/core/validation/validators/date.d.ts +0 -38
  207. package/dist/core/validation/validators/date.js +0 -117
  208. package/dist/core/validation/validators/email.js +0 -60
  209. package/dist/core/validation/validators/index.js +0 -14
  210. package/dist/core/validation/validators/max-length.js +0 -60
  211. package/dist/core/validation/validators/max.js +0 -60
  212. package/dist/core/validation/validators/min-length.js +0 -60
  213. package/dist/core/validation/validators/min.js +0 -60
  214. package/dist/core/validation/validators/number.js +0 -90
  215. package/dist/core/validation/validators/pattern.js +0 -62
  216. package/dist/core/validation/validators/phone.js +0 -58
  217. package/dist/core/validation/validators/required.js +0 -69
  218. package/dist/core/validation/validators/url.js +0 -55
  219. package/dist/create-field-path-CdPF3lIK.js +0 -704
  220. package/dist/hooks/useFormControl.js +0 -298
  221. package/dist/node-factory-D7DOnSSN.js +0 -3200
package/dist/index.js CHANGED
@@ -1,8 +1,3271 @@
1
- export * from './core/types';
2
- export * from './core/factories';
3
- export * from './core/utils';
4
- export { FormNode } from './core/nodes/form-node';
5
- export { FieldNode } from './core/nodes/field-node';
6
- export { GroupNode } from './core/nodes/group-node';
7
- export { ArrayNode } from './core/nodes/array-node';
8
- export { useFormControl, useFormControlValue } from './hooks/useFormControl';
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 {
12
+ // ============================================================================
13
+ // Protected состояние (для Template Method паттерна)
14
+ // ============================================================================
15
+ /**
16
+ * Пользователь взаимодействовал с узлом (touched)
17
+ * Protected: наследники могут читать/изменять через методы
18
+ */
19
+ _touched = g(!1);
20
+ /**
21
+ * Значение узла было изменено (dirty)
22
+ * Protected: наследники могут читать/изменять через методы
23
+ */
24
+ _dirty = g(!1);
25
+ /**
26
+ * Текущий статус узла
27
+ * Protected: наследники могут читать/изменять через методы
28
+ */
29
+ _status = g("valid");
30
+ // ============================================================================
31
+ // Публичные computed signals (readonly для внешнего мира)
32
+ // ============================================================================
33
+ /**
34
+ * Пользователь взаимодействовал с узлом (touched)
35
+ * Computed из _touched для предоставления readonly интерфейса
36
+ */
37
+ touched = d(() => this._touched.value);
38
+ /**
39
+ * Пользователь не взаимодействовал с узлом (untouched)
40
+ */
41
+ untouched = d(() => !this._touched.value);
42
+ /**
43
+ * Значение узла было изменено (dirty)
44
+ * Computed из _dirty для предоставления readonly интерфейса
45
+ */
46
+ dirty = d(() => this._dirty.value);
47
+ /**
48
+ * Значение узла не было изменено (pristine)
49
+ */
50
+ pristine = d(() => !this._dirty.value);
51
+ /**
52
+ * Текущий статус узла
53
+ * Computed из _status для предоставления readonly интерфейса
54
+ */
55
+ status = d(() => this._status.value);
56
+ /**
57
+ * Узел отключен (disabled)
58
+ */
59
+ disabled = d(() => this._status.value === "disabled");
60
+ /**
61
+ * Узел включен (enabled)
62
+ */
63
+ enabled = d(() => this._status.value !== "disabled");
64
+ /**
65
+ * Получить ошибки валидации с фильтрацией
66
+ *
67
+ * Позволяет фильтровать ошибки по различным критериям:
68
+ * - По коду ошибки
69
+ * - По сообщению (частичное совпадение)
70
+ * - По параметрам
71
+ * - Через кастомный предикат
72
+ *
73
+ * Без параметров возвращает все ошибки (эквивалент errors.value)
74
+ *
75
+ * @param options - Опции фильтрации ошибок
76
+ * @returns Отфильтрованный массив ошибок валидации
77
+ *
78
+ * @example
79
+ * ```typescript
80
+ * // Все ошибки
81
+ * const allErrors = form.getErrors();
82
+ *
83
+ * // Ошибки с конкретным кодом
84
+ * const requiredErrors = form.getErrors({ code: 'required' });
85
+ *
86
+ * // Ошибки с несколькими кодами
87
+ * const errors = form.getErrors({ code: ['required', 'email'] });
88
+ *
89
+ * // Ошибки по сообщению
90
+ * const passwordErrors = form.getErrors({ message: 'Password' });
91
+ *
92
+ * // Ошибки по параметрам
93
+ * const minLengthErrors = form.getErrors({
94
+ * params: { minLength: 8 }
95
+ * });
96
+ *
97
+ * // Кастомная фильтрация
98
+ * const customErrors = form.getErrors({
99
+ * predicate: (err) => err.code.startsWith('custom_')
100
+ * });
101
+ * ```
102
+ */
103
+ getErrors(e) {
104
+ const t = this.errors.value;
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()))
107
+ return !1;
108
+ if (e.params !== void 0) {
109
+ if (!s.params)
110
+ return !1;
111
+ for (const [i, r] of Object.entries(e.params))
112
+ if (s.params[i] !== r)
113
+ return !1;
114
+ }
115
+ return !(e.predicate !== void 0 && !e.predicate(s));
116
+ }) : t;
117
+ }
118
+ // ============================================================================
119
+ // Методы управления состоянием (Template Method)
120
+ // ============================================================================
121
+ /**
122
+ * Отметить узел как touched (пользователь взаимодействовал)
123
+ *
124
+ * Template Method: обновляет signal в базовом классе,
125
+ * вызывает hook для кастомной логики в наследниках
126
+ */
127
+ markAsTouched() {
128
+ this._touched.value = !0, this.onMarkAsTouched();
129
+ }
130
+ /**
131
+ * Отметить узел как untouched
132
+ *
133
+ * Template Method: обновляет signal в базовом классе,
134
+ * вызывает hook для кастомной логики в наследниках
135
+ */
136
+ markAsUntouched() {
137
+ this._touched.value = !1, this.onMarkAsUntouched();
138
+ }
139
+ /**
140
+ * Отметить узел как dirty (значение изменено)
141
+ *
142
+ * Template Method: обновляет signal в базовом классе,
143
+ * вызывает hook для кастомной логики в наследниках
144
+ */
145
+ markAsDirty() {
146
+ this._dirty.value = !0, this.onMarkAsDirty();
147
+ }
148
+ /**
149
+ * Отметить узел как pristine (значение не изменено)
150
+ *
151
+ * Template Method: обновляет signal в базовом классе,
152
+ * вызывает hook для кастомной логики в наследниках
153
+ */
154
+ markAsPristine() {
155
+ this._dirty.value = !1, this.onMarkAsPristine();
156
+ }
157
+ /**
158
+ * Пометить все поля (включая вложенные) как touched
159
+ * Алиас для markAsTouched(), но более явно показывает намерение
160
+ * пометить ВСЕ поля рекурсивно
161
+ *
162
+ * Полезно для:
163
+ * - Показа всех ошибок валидации перед submit
164
+ * - Принудительного отображения ошибок при нажатии "Validate All"
165
+ * - Отображения невалидных полей в wizard/step form
166
+ *
167
+ * @example
168
+ * ```typescript
169
+ * // Показать все ошибки перед submit
170
+ * form.touchAll();
171
+ * const isValid = await form.validate();
172
+ * if (!isValid) {
173
+ * // Все ошибки теперь видны пользователю
174
+ * }
175
+ *
176
+ * // Или использовать submit() который уже вызывает touchAll
177
+ * await form.submit(async (values) => {
178
+ * await api.save(values);
179
+ * });
180
+ * ```
181
+ */
182
+ touchAll() {
183
+ this.markAsTouched();
184
+ }
185
+ // ============================================================================
186
+ // Методы управления доступностью (Template Method)
187
+ // ============================================================================
188
+ /**
189
+ * Отключить узел
190
+ *
191
+ * Template Method: обновляет статус в базовом классе,
192
+ * вызывает hook для кастомной логики в наследниках
193
+ *
194
+ * Отключенные узлы не проходят валидацию и не включаются в getValue()
195
+ */
196
+ disable() {
197
+ this._status.value = "disabled", this.onDisable();
198
+ }
199
+ /**
200
+ * Включить узел
201
+ *
202
+ * Template Method: обновляет статус в базовом классе,
203
+ * вызывает hook для кастомной логики в наследниках
204
+ */
205
+ enable() {
206
+ this._status.value = "valid", this.onEnable();
207
+ }
208
+ // ============================================================================
209
+ // Protected hooks (для переопределения в наследниках)
210
+ // ============================================================================
211
+ /**
212
+ * Hook: вызывается после markAsTouched()
213
+ *
214
+ * Переопределите в наследниках для дополнительной логики:
215
+ * - GroupNode: пометить все дочерние узлы как touched
216
+ * - ArrayNode: пометить все элементы массива как touched
217
+ * - FieldNode: пустая реализация (нет дочерних узлов)
218
+ *
219
+ * @example
220
+ * ```typescript
221
+ * // GroupNode
222
+ * protected onMarkAsTouched(): void {
223
+ * this.fields.forEach(field => field.markAsTouched());
224
+ * }
225
+ * ```
226
+ */
227
+ onMarkAsTouched() {
228
+ }
229
+ /**
230
+ * Hook: вызывается после markAsUntouched()
231
+ *
232
+ * Переопределите в наследниках для дополнительной логики:
233
+ * - GroupNode: пометить все дочерние узлы как untouched
234
+ * - ArrayNode: пометить все элементы массива как untouched
235
+ * - FieldNode: пустая реализация (нет дочерних узлов)
236
+ */
237
+ onMarkAsUntouched() {
238
+ }
239
+ /**
240
+ * Hook: вызывается после markAsDirty()
241
+ *
242
+ * Переопределите в наследниках для дополнительной логики:
243
+ * - GroupNode: может обновить родительскую форму
244
+ * - ArrayNode: может обновить родительскую форму
245
+ * - FieldNode: пустая реализация
246
+ */
247
+ onMarkAsDirty() {
248
+ }
249
+ /**
250
+ * Hook: вызывается после markAsPristine()
251
+ *
252
+ * Переопределите в наследниках для дополнительной логики:
253
+ * - GroupNode: пометить все дочерние узлы как pristine
254
+ * - ArrayNode: пометить все элементы массива как pristine
255
+ * - FieldNode: пустая реализация
256
+ */
257
+ onMarkAsPristine() {
258
+ }
259
+ /**
260
+ * Hook: вызывается после disable()
261
+ *
262
+ * Переопределите в наследниках для дополнительной логики:
263
+ * - GroupNode: отключить все дочерние узлы
264
+ * - ArrayNode: отключить все элементы массива
265
+ * - FieldNode: очистить ошибки валидации
266
+ *
267
+ * @example
268
+ * ```typescript
269
+ * // GroupNode
270
+ * protected onDisable(): void {
271
+ * this.fields.forEach(field => field.disable());
272
+ * }
273
+ * ```
274
+ */
275
+ onDisable() {
276
+ }
277
+ /**
278
+ * Hook: вызывается после enable()
279
+ *
280
+ * Переопределите в наследниках для дополнительной логики:
281
+ * - GroupNode: включить все дочерние узлы
282
+ * - ArrayNode: включить все элементы массива
283
+ * - FieldNode: пустая реализация
284
+ */
285
+ onEnable() {
286
+ }
287
+ }
288
+ class D {
289
+ /**
290
+ * Хранилище подписок
291
+ * Ключ: уникальный идентификатор подписки
292
+ * Значение: функция отписки (dispose)
293
+ */
294
+ subscriptions = /* @__PURE__ */ new Map();
295
+ /**
296
+ * Добавляет подписку
297
+ *
298
+ * Если подписка с таким ключом уже существует, отписывается от неё
299
+ * и заменяет новой. Это предотвращает утечки памяти при повторной
300
+ * регистрации подписки с тем же ключом.
301
+ *
302
+ * @param key Уникальный ключ подписки
303
+ * @param dispose Функция отписки (обычно возвращаемая из effect())
304
+ * @returns Функция для отписки от этой конкретной подписки
305
+ *
306
+ * @example
307
+ * ```typescript
308
+ * const manager = new SubscriptionManager();
309
+ *
310
+ * // Добавление подписки
311
+ * const unsubscribe = manager.add('mySubscription', () => {
312
+ * console.log('Disposing subscription');
313
+ * });
314
+ *
315
+ * // Отписка через возвращаемую функцию
316
+ * unsubscribe();
317
+ *
318
+ * // Или через manager.remove()
319
+ * manager.add('anotherSub', disposeFn);
320
+ * manager.remove('anotherSub');
321
+ * ```
322
+ */
323
+ add(e, t) {
324
+ return this.subscriptions.has(e) && (process.env.NODE_ENV !== "production" && console.warn(`[SubscriptionManager] Subscription "${e}" already exists, replacing`), this.subscriptions.get(e)?.()), this.subscriptions.set(e, t), () => this.remove(e);
325
+ }
326
+ /**
327
+ * Удаляет подписку по ключу
328
+ *
329
+ * Вызывает функцию отписки и удаляет подписку из хранилища.
330
+ * Если подписка с таким ключом не найдена, ничего не делает.
331
+ *
332
+ * @param key Ключ подписки
333
+ * @returns true, если подписка была удалена, false если не найдена
334
+ *
335
+ * @example
336
+ * ```typescript
337
+ * const manager = new SubscriptionManager();
338
+ * manager.add('mySub', disposeFn);
339
+ *
340
+ * // Удаление подписки
341
+ * const removed = manager.remove('mySub'); // true
342
+ *
343
+ * // Попытка удалить несуществующую подписку
344
+ * const removed2 = manager.remove('nonExistent'); // false
345
+ * ```
346
+ */
347
+ remove(e) {
348
+ const t = this.subscriptions.get(e);
349
+ return t ? (t(), this.subscriptions.delete(e), !0) : !1;
350
+ }
351
+ /**
352
+ * Очищает все подписки
353
+ *
354
+ * Вызывает функции отписки для всех активных подписок
355
+ * и очищает хранилище. Обычно используется при dispose узла формы.
356
+ *
357
+ * @example
358
+ * ```typescript
359
+ * class FieldNode {
360
+ * private subscriptions = new SubscriptionManager();
361
+ *
362
+ * dispose() {
363
+ * // Отписываемся от всех effect
364
+ * this.subscriptions.clear();
365
+ * }
366
+ * }
367
+ * ```
368
+ */
369
+ clear() {
370
+ this.subscriptions.forEach((e) => {
371
+ e();
372
+ }), this.subscriptions.clear();
373
+ }
374
+ /**
375
+ * Возвращает количество активных подписок
376
+ *
377
+ * Полезно для отладки утечек памяти. Если количество подписок
378
+ * растет без ограничений, это может указывать на то, что
379
+ * компоненты не отписываются должным образом.
380
+ *
381
+ * @returns Количество активных подписок
382
+ *
383
+ * @example
384
+ * ```typescript
385
+ * const manager = new SubscriptionManager();
386
+ * console.log(manager.size()); // 0
387
+ *
388
+ * manager.add('sub1', disposeFn1);
389
+ * manager.add('sub2', disposeFn2);
390
+ * console.log(manager.size()); // 2
391
+ *
392
+ * manager.clear();
393
+ * console.log(manager.size()); // 0
394
+ * ```
395
+ */
396
+ size() {
397
+ return this.subscriptions.size;
398
+ }
399
+ /**
400
+ * Проверяет, есть ли подписка с данным ключом
401
+ *
402
+ * @param key Ключ подписки
403
+ * @returns true, если подписка существует
404
+ *
405
+ * @example
406
+ * ```typescript
407
+ * const manager = new SubscriptionManager();
408
+ * manager.add('mySub', disposeFn);
409
+ *
410
+ * console.log(manager.has('mySub')); // true
411
+ * console.log(manager.has('nonExistent')); // false
412
+ * ```
413
+ */
414
+ has(e) {
415
+ return this.subscriptions.has(e);
416
+ }
417
+ /**
418
+ * Возвращает список всех ключей активных подписок
419
+ *
420
+ * Полезно для отладки: можно увидеть, какие подписки активны.
421
+ *
422
+ * @returns Массив ключей всех активных подписок
423
+ *
424
+ * @example
425
+ * ```typescript
426
+ * const manager = new SubscriptionManager();
427
+ * manager.add('watch-value', disposeFn1);
428
+ * manager.add('watch-errors', disposeFn2);
429
+ *
430
+ * console.log(manager.getKeys()); // ['watch-value', 'watch-errors']
431
+ * ```
432
+ */
433
+ getKeys() {
434
+ return Array.from(this.subscriptions.keys());
435
+ }
436
+ /**
437
+ * Отписывается от всех подписок (алиас для clear())
438
+ *
439
+ * Используется при dispose() узла формы для совместимости с ожидаемым API.
440
+ *
441
+ * @example
442
+ * ```typescript
443
+ * class FieldNode {
444
+ * private subscriptions = new SubscriptionManager();
445
+ *
446
+ * dispose() {
447
+ * this.subscriptions.dispose();
448
+ * }
449
+ * }
450
+ * ```
451
+ */
452
+ dispose() {
453
+ this.clear();
454
+ }
455
+ }
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;
487
+ /**
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
+ * Начать валидацию
495
+ *
496
+ * Переводит статус в 'pending' если поле не отключено
497
+ *
498
+ * @example
499
+ * ```typescript
500
+ * statusMachine.startValidation();
501
+ * // status: 'pending'
502
+ * ```
503
+ */
504
+ startValidation() {
505
+ this._status.value !== "disabled" && (this._status.value = "pending");
506
+ }
507
+ /**
508
+ * Завершить валидацию
509
+ *
510
+ * @param hasErrors - Есть ли ошибки валидации
511
+ *
512
+ * @example
513
+ * ```typescript
514
+ * // Валидация успешна
515
+ * statusMachine.completeValidation(false);
516
+ * // status: 'valid'
517
+ *
518
+ * // Есть ошибки
519
+ * statusMachine.completeValidation(true);
520
+ * // status: 'invalid'
521
+ * ```
522
+ */
523
+ completeValidation(e) {
524
+ (this._status.value === "pending" || this._status.value !== "disabled") && (this._status.value = e ? "invalid" : "valid");
525
+ }
526
+ /**
527
+ * Установить ошибки напрямую (без перехода через pending)
528
+ *
529
+ * Используется для синхронной валидации или установки ошибок извне
530
+ *
531
+ * @param hasErrors - Есть ли ошибки
532
+ *
533
+ * @example
534
+ * ```typescript
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
+ * Отключить поле
544
+ *
545
+ * Переводит статус в 'disabled'
546
+ *
547
+ * @example
548
+ * ```typescript
549
+ * statusMachine.disable();
550
+ * // status: 'disabled'
551
+ * ```
552
+ */
553
+ disable() {
554
+ this._status.value = "disabled";
555
+ }
556
+ /**
557
+ * Включить поле
558
+ *
559
+ * @param hasErrors - Есть ли ошибки (определяет valid/invalid)
560
+ *
561
+ * @example
562
+ * ```typescript
563
+ * statusMachine.enable(false);
564
+ * // status: 'valid'
565
+ *
566
+ * statusMachine.enable(true);
567
+ * // status: 'invalid'
568
+ * ```
569
+ */
570
+ enable(e = !1) {
571
+ this._status.value = e ? "invalid" : "valid";
572
+ }
573
+ /**
574
+ * Обработать событие (альтернативный API)
575
+ *
576
+ * @param event - Событие для обработки
577
+ *
578
+ * @example
579
+ * ```typescript
580
+ * statusMachine.dispatch({ type: 'START_VALIDATION' });
581
+ * statusMachine.dispatch({ type: 'VALIDATION_FAILURE' });
582
+ * ```
583
+ */
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";
617
+ }
618
+ }
619
+ class ee extends C {
620
+ // ============================================================================
621
+ // Приватные сигналы
622
+ // ============================================================================
623
+ _value;
624
+ _errors;
625
+ // _touched, _dirty наследуются от FormNode (protected)
626
+ // _status управляется через statusMachine
627
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
628
+ _componentProps;
629
+ /**
630
+ * State machine для управления статусом поля
631
+ * Централизует логику переходов между valid/invalid/pending/disabled
632
+ */
633
+ statusMachine;
634
+ // ============================================================================
635
+ // Публичные computed signals
636
+ // ============================================================================
637
+ value;
638
+ // valid, invalid, pending, status, disabled берутся из statusMachine
639
+ valid;
640
+ invalid;
641
+ pending;
642
+ // Override status и disabled из базового класса
643
+ status;
644
+ disabled;
645
+ // touched, dirty наследуются от FormNode
646
+ errors;
647
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
648
+ componentProps;
649
+ /**
650
+ * Вычисляемое свойство: нужно ли показывать ошибку
651
+ * Ошибка показывается если поле невалидно И (touched ИЛИ dirty)
652
+ */
653
+ shouldShowError;
654
+ // ============================================================================
655
+ // Конфигурация
656
+ // ============================================================================
657
+ validators;
658
+ asyncValidators;
659
+ updateOn;
660
+ initialValue;
661
+ currentAbortController;
662
+ debounceMs;
663
+ validateDebounceTimer;
664
+ /**
665
+ * Pending debounced validation state
666
+ * Contains resolve function and AbortController for cancellation
667
+ */
668
+ pendingValidation;
669
+ /**
670
+ * Менеджер подписок для централизованного cleanup
671
+ * Использует SubscriptionManager вместо массива для управления подписками
672
+ */
673
+ disposers = new D();
674
+ component;
675
+ // ============================================================================
676
+ // Конструктор
677
+ // ============================================================================
678
+ constructor(e) {
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)
681
+ );
682
+ }
683
+ // ============================================================================
684
+ // Реализация абстрактных методов FormNode
685
+ // ============================================================================
686
+ getValue() {
687
+ return this._value.peek();
688
+ }
689
+ setValue(e, t) {
690
+ if (this._value.value = e, this._dirty.value = !0, t?.emitEvent === !1)
691
+ return;
692
+ const s = this.validators.length > 0 || this.asyncValidators.length > 0, i = this._errors.value.length > 0;
693
+ if (this.updateOn === "change") {
694
+ this.validate();
695
+ return;
696
+ }
697
+ i && s && this.validate();
698
+ }
699
+ patchValue(e) {
700
+ this.setValue(e);
701
+ }
702
+ /**
703
+ * Сбросить поле к указанному значению (или к initialValue)
704
+ *
705
+ * @param value - опциональное значение для сброса. Если не указано, используется initialValue
706
+ *
707
+ * @remarks
708
+ * Этот метод:
709
+ * - Устанавливает значение в value или initialValue
710
+ * - Очищает ошибки валидации
711
+ * - Сбрасывает touched/dirty флаги
712
+ * - Устанавливает статус в 'valid'
713
+ *
714
+ * Если вам нужно сбросить к исходному значению, используйте resetToInitial()
715
+ *
716
+ * @example
717
+ * ```typescript
718
+ * // Сброс к initialValue
719
+ * field.reset();
720
+ *
721
+ * // Сброс к новому значению
722
+ * field.reset('new value');
723
+ * ```
724
+ */
725
+ reset(e) {
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);
727
+ }
728
+ /**
729
+ * Сбросить поле к исходному значению (initialValue)
730
+ *
731
+ * @remarks
732
+ * Алиас для reset() без параметров, но более явный:
733
+ * - resetToInitial() - явно показывает намерение вернуться к начальному значению
734
+ * - reset() - может принимать новое значение
735
+ *
736
+ * Полезно когда:
737
+ * - Пользователь нажал "Cancel" - вернуть форму в исходное состояние
738
+ * - Форма была изменена через reset(newValue), но нужно вернуться к самому началу
739
+ * - Явное намерение показать "отмену всех изменений"
740
+ *
741
+ * @example
742
+ * ```typescript
743
+ * const field = new FieldNode({ value: 'initial', component: Input });
744
+ *
745
+ * field.setValue('changed');
746
+ * field.reset('temp value');
747
+ * console.log(field.value.value); // 'temp value'
748
+ *
749
+ * field.resetToInitial();
750
+ * console.log(field.value.value); // 'initial'
751
+ * ```
752
+ */
753
+ resetToInitial() {
754
+ this.reset(this.initialValue);
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
+ }
768
+ /**
769
+ * Запустить валидацию поля
770
+ * @param options - опции валидации
771
+ * @returns `Promise<boolean>` - true если поле валидно
772
+ *
773
+ * @remarks
774
+ * Метод защищен от race conditions через AbortController.
775
+ * При быстром вводе только последняя валидация применяет результаты.
776
+ *
777
+ * @example
778
+ * ```typescript
779
+ * // Обычная валидация
780
+ * await field.validate();
781
+ *
782
+ * // С debounce
783
+ * await field.validate({ debounce: 300 });
784
+ * ```
785
+ */
786
+ async validate(e) {
787
+ const t = e?.debounce ?? this.debounceMs;
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) {
794
+ i(!1);
795
+ return;
796
+ }
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
+ });
808
+ }
809
+ /**
810
+ * Немедленная валидация без debounce
811
+ * @private
812
+ * @param providedController - AbortController from debounced validate()
813
+ * @remarks
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);
826
+ }
827
+ if (s.aborted)
828
+ return !1;
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)
833
+ return !1;
834
+ }
835
+ if (this.asyncValidators.length > 0) {
836
+ if (s.aborted)
837
+ return !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
+ }
874
+ }
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"));
876
+ }
877
+ setErrors(e) {
878
+ this._errors.value = e;
879
+ const t = e.some((s) => s.severity !== "warning");
880
+ this.statusMachine.setErrors(t);
881
+ }
882
+ clearErrors() {
883
+ this._errors.value = [], this.statusMachine.setErrors(!1);
884
+ }
885
+ // ============================================================================
886
+ // Protected hooks (Template Method pattern)
887
+ // ============================================================================
888
+ /**
889
+ * Hook: вызывается после markAsTouched()
890
+ *
891
+ * Для FieldNode: если updateOn === 'blur', запускаем валидацию
892
+ */
893
+ onMarkAsTouched() {
894
+ this.updateOn === "blur" && this.validate();
895
+ }
896
+ /**
897
+ * Hook: вызывается после disable()
898
+ *
899
+ * Для FieldNode: синхронизируем statusMachine и очищаем ошибки
900
+ */
901
+ onDisable() {
902
+ this.statusMachine.disable(), this._errors.value = [];
903
+ }
904
+ /**
905
+ * Hook: вызывается после enable()
906
+ *
907
+ * Для FieldNode: синхронизируем statusMachine и запускаем валидацию
908
+ */
909
+ onEnable() {
910
+ this.statusMachine.enable(this._errors.value.length > 0), this.validate();
911
+ }
912
+ /**
913
+ * Обновляет свойства компонента (например, опции для Select)
914
+ *
915
+ * @example
916
+ * ```typescript
917
+ * // Обновление опций для Select после загрузки справочников
918
+ * form.registrationAddress.city.updateComponentProps({
919
+ * options: cities
920
+ * });
921
+ * ```
922
+ */
923
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
924
+ updateComponentProps(e) {
925
+ this._componentProps.value = {
926
+ ...this._componentProps.value,
927
+ ...e
928
+ };
929
+ }
930
+ /**
931
+ * Динамически изменяет триггер валидации (updateOn)
932
+ * Полезно для адаптивной валидации - например, переключиться на instant feedback после первого submit
933
+ *
934
+ * @param updateOn - новый триггер валидации: 'change' | 'blur' | 'submit'
935
+ *
936
+ * @example
937
+ * ```typescript
938
+ * // Сценарий 1: Instant feedback после submit
939
+ * const form = new GroupNode({
940
+ * email: {
941
+ * value: '',
942
+ * component: Input,
943
+ * updateOn: 'submit', // Изначально валидация только при submit
944
+ * validators: [required, email]
945
+ * }
946
+ * });
947
+ *
948
+ * await form.submit(async (values) => {
949
+ * // После submit переключаем на instant feedback
950
+ * form.email.setUpdateOn('change');
951
+ * await api.save(values);
952
+ * });
953
+ *
954
+ * // Теперь валидация происходит при каждом изменении
955
+ *
956
+ * // Сценарий 2: Прогрессивное улучшение
957
+ * form.email.setUpdateOn('blur'); // Сначала только при blur
958
+ * // ... пользователь начинает вводить ...
959
+ * form.email.setUpdateOn('change'); // Переключаем на change для real-time feedback
960
+ * ```
961
+ */
962
+ setUpdateOn(e) {
963
+ this.updateOn = e;
964
+ }
965
+ getUpdateOn() {
966
+ return this.updateOn;
967
+ }
968
+ // ============================================================================
969
+ // Методы-помощники для реактивности (Фаза 1)
970
+ // ============================================================================
971
+ /**
972
+ * Подписка на изменения значения поля
973
+ * Автоматически отслеживает изменения через @preact/signals effect
974
+ *
975
+ * @param callback - Функция, вызываемая при изменении значения.
976
+ * Для async операций передается AbortSignal во втором параметре.
977
+ * @returns Функция отписки для cleanup
978
+ *
979
+ * @example
980
+ * ```typescript
981
+ * // Синхронный callback
982
+ * const unsubscribe = form.email.watch((value) => {
983
+ * console.log('Email changed:', value);
984
+ * });
985
+ *
986
+ * // Асинхронный callback с поддержкой отмены
987
+ * const unsubscribe = form.email.watch(async (value, signal) => {
988
+ * const result = await fetch('/api/validate', { signal });
989
+ * // ...
990
+ * });
991
+ *
992
+ * // Cleanup
993
+ * useEffect(() => unsubscribe, []);
994
+ * ```
995
+ */
996
+ watch(e) {
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
+ });
1004
+ }
1005
+ /**
1006
+ * Вычисляемое значение из других полей
1007
+ * Автоматически обновляет текущее поле при изменении источников
1008
+ *
1009
+ * @param sources - Массив ReadonlySignal для отслеживания
1010
+ * @param computeFn - Функция вычисления нового значения
1011
+ * @returns Функция отписки для cleanup
1012
+ *
1013
+ * @example
1014
+ * ```typescript
1015
+ * // Автоматический расчет первоначального взноса (20% от стоимости)
1016
+ * const dispose = form.initialPayment.computeFrom(
1017
+ * [form.propertyValue.value],
1018
+ * (propertyValue) => {
1019
+ * return propertyValue ? propertyValue * 0.2 : null;
1020
+ * }
1021
+ * );
1022
+ *
1023
+ * // Cleanup
1024
+ * useEffect(() => dispose, []);
1025
+ * ```
1026
+ */
1027
+ computeFrom(e, t) {
1028
+ const s = m(() => {
1029
+ const r = e.map((o) => o.value), n = t(...r);
1030
+ this.setValue(n, { emitEvent: !1 });
1031
+ }), i = A(w.ComputeFrom);
1032
+ return this.disposers.add(i, s);
1033
+ }
1034
+ /**
1035
+ * Очистить все ресурсы и таймеры
1036
+ * Должен вызываться при unmount компонента
1037
+ *
1038
+ * @remarks
1039
+ * Освобождает все ресурсы:
1040
+ * - Отписывает все subscriptions через SubscriptionManager
1041
+ * - Отменяет pending/running валидации через cancelPendingValidation()
1042
+ *
1043
+ * Использует try-finally для гарантированного cleanup даже при ошибках.
1044
+ *
1045
+ * @example
1046
+ * ```typescript
1047
+ * useEffect(() => {
1048
+ * return () => {
1049
+ * field.dispose();
1050
+ * };
1051
+ * }, []);
1052
+ * ```
1053
+ */
1054
+ dispose() {
1055
+ try {
1056
+ this.disposers.dispose();
1057
+ } finally {
1058
+ this.cancelPendingValidation();
1059
+ }
1060
+ }
1061
+ }
1062
+ class te {
1063
+ form;
1064
+ constructor(e) {
1065
+ this.form = e;
1066
+ }
1067
+ /**
1068
+ * Применить валидаторы к полям формы
1069
+ *
1070
+ * Этапы применения:
1071
+ * 1. Разделение валидаторов на field и tree
1072
+ * 2. Применение field валидаторов (sync/async)
1073
+ * 3. Применение tree валидаторов (кросс-полевая валидация)
1074
+ *
1075
+ * @param validators Зарегистрированные валидаторы
1076
+ */
1077
+ async apply(e) {
1078
+ const { validatorsByField: t, treeValidators: s } = this.groupValidators(e);
1079
+ await this.applyFieldValidators(t), this.applyTreeValidators(s);
1080
+ }
1081
+ /**
1082
+ * Группировка валидаторов по типам
1083
+ *
1084
+ * Разделяет валидаторы на:
1085
+ * - Field validators (sync/async) - группируются по fieldPath
1086
+ * - Tree validators - применяются ко всей форме
1087
+ *
1088
+ * @param validators Все зарегистрированные валидаторы
1089
+ * @returns Сгруппированные валидаторы
1090
+ */
1091
+ groupValidators(e) {
1092
+ const t = /* @__PURE__ */ new Map(), s = [];
1093
+ for (const i of e)
1094
+ if (i.type === "tree")
1095
+ s.push(i);
1096
+ else {
1097
+ const r = t.get(i.fieldPath) || [];
1098
+ r.push(i), t.set(i.fieldPath, r);
1099
+ }
1100
+ return { validatorsByField: t, treeValidators: s };
1101
+ }
1102
+ /**
1103
+ * Применение field валидаторов к полям
1104
+ *
1105
+ * Для каждого поля:
1106
+ * 1. Найти FieldNode по пути
1107
+ * 2. Проверить условия (condition)
1108
+ * 3. Выполнить sync/async валидаторы
1109
+ * 4. Установить ошибки на поле
1110
+ *
1111
+ * @param validatorsByField Валидаторы, сгруппированные по полям
1112
+ */
1113
+ async applyFieldValidators(e) {
1114
+ for (const [t, s] of e) {
1115
+ const i = this.form.getFieldByPath(t);
1116
+ if (!i) {
1117
+ console.warn(`Field ${t} not found in GroupNode`);
1118
+ continue;
1119
+ }
1120
+ if (!M(i) && !N(i)) {
1121
+ process.env.NODE_ENV !== "production" && console.warn(`Validation can only run on FieldNode or ArrayNode, skipping ${t}`);
1122
+ continue;
1123
+ }
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)
1132
+ if (!(o.condition && !this.checkCondition(o.condition)))
1133
+ try {
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,
1147
+ `ValidationApplicator: validator for ${t}`,
1148
+ V.LOG
1149
+ );
1150
+ }
1151
+ r.length > 0 ? i.setErrors(r) : i.clearErrors();
1152
+ }
1153
+ }
1154
+ /**
1155
+ * Применение tree валидаторов (кросс-полевая валидация)
1156
+ *
1157
+ * Tree валидаторы имеют доступ ко всей форме через TreeValidationContext.
1158
+ * Ошибки устанавливаются на targetField (если указано).
1159
+ *
1160
+ * @param treeValidators Список tree валидаторов
1161
+ */
1162
+ applyTreeValidators(e) {
1163
+ for (const t of e) {
1164
+ const s = new Y(this.form);
1165
+ if (!(t.condition && !this.checkCondition(t.condition)))
1166
+ try {
1167
+ if (t.type !== "tree")
1168
+ continue;
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]);
1177
+ }
1178
+ }
1179
+ }
1180
+ } catch (i) {
1181
+ S.handle(i, "ValidationApplicator: tree validator", V.LOG);
1182
+ }
1183
+ }
1184
+ }
1185
+ /**
1186
+ * Проверка условия (condition) для валидатора
1187
+ *
1188
+ * Условие определяет, должен ли валидатор выполняться.
1189
+ * Использует getFieldByPath для поддержки вложенных путей.
1190
+ *
1191
+ * @param condition Условие валидатора
1192
+ * @returns true, если условие выполнено
1193
+ */
1194
+ checkCondition(e) {
1195
+ const t = this.form.getFieldByPath(e.fieldPath);
1196
+ if (!t)
1197
+ return !1;
1198
+ const s = t.value.value;
1199
+ return e.conditionFn(s);
1200
+ }
1201
+ }
1202
+ class se {
1203
+ /**
1204
+ * Парсит путь в массив сегментов
1205
+ *
1206
+ * Поддерживаемые форматы:
1207
+ * - Простые пути: "name", "email"
1208
+ * - Вложенные пути: "address.city", "user.profile.avatar"
1209
+ * - Массивы: "items[0]", "items[0].name", "tags[1][0]"
1210
+ * - Комбинации: "orders[0].items[1].price"
1211
+ *
1212
+ * @param path Путь к полю (строка с точками и квадратными скобками)
1213
+ * @returns Массив сегментов пути
1214
+ *
1215
+ * @example
1216
+ * ```typescript
1217
+ * navigator.parsePath('email');
1218
+ * // [{ key: 'email' }]
1219
+ *
1220
+ * navigator.parsePath('address.city');
1221
+ * // [{ key: 'address' }, { key: 'city' }]
1222
+ *
1223
+ * navigator.parsePath('items[0].name');
1224
+ * // [{ key: 'items', index: 0 }, { key: 'name' }]
1225
+ * ```
1226
+ */
1227
+ parsePath(e) {
1228
+ const t = [];
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;
1233
+ }
1234
+ return s && this.addSegment(t, s), t;
1235
+ }
1236
+ /**
1237
+ * Добавляет сегмент в массив, обрабатывая массивы
1238
+ * @private
1239
+ */
1240
+ addSegment(e, t) {
1241
+ const s = t.match(/^(.+)\[(\d+)\]$/);
1242
+ s ? e.push({
1243
+ key: s[1],
1244
+ index: parseInt(s[2], 10)
1245
+ }) : e.push({ key: t });
1246
+ }
1247
+ /**
1248
+ * Получает значение по пути из объекта
1249
+ *
1250
+ * Проходит по всем сегментам пути и возвращает конечное значение.
1251
+ * Если путь не найден, возвращает undefined.
1252
+ *
1253
+ * @param obj Объект для навигации
1254
+ * @param path Путь к значению
1255
+ * @returns Значение или undefined, если путь не найден
1256
+ *
1257
+ * @example
1258
+ * ```typescript
1259
+ * const obj = {
1260
+ * email: 'test@mail.com',
1261
+ * address: { city: 'Moscow' },
1262
+ * items: [{ title: 'Item 1' }]
1263
+ * };
1264
+ *
1265
+ * navigator.getValueByPath(obj, 'email');
1266
+ * // 'test@mail.com'
1267
+ *
1268
+ * navigator.getValueByPath(obj, 'address.city');
1269
+ * // 'Moscow'
1270
+ *
1271
+ * navigator.getValueByPath(obj, 'items[0].title');
1272
+ * // 'Item 1'
1273
+ *
1274
+ * navigator.getValueByPath(obj, 'invalid.path');
1275
+ * // undefined
1276
+ * ```
1277
+ */
1278
+ getValueByPath(e, t) {
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];
1286
+ }
1287
+ }
1288
+ return i;
1289
+ }
1290
+ /**
1291
+ * Устанавливает значение по пути в объекте (мутирует объект)
1292
+ *
1293
+ * Создает промежуточные объекты, если они не существуют.
1294
+ * Выбрасывает ошибку, если ожидается массив, но его нет.
1295
+ *
1296
+ * @param obj Объект для модификации
1297
+ * @param path Путь к значению
1298
+ * @param value Новое значение
1299
+ *
1300
+ * @throws {Error} Если ожидается массив по пути, но его нет
1301
+ *
1302
+ * @example
1303
+ * ```typescript
1304
+ * const obj = { address: { city: '' } };
1305
+ * navigator.setValueByPath(obj, 'address.city', 'Moscow');
1306
+ * // obj.address.city === 'Moscow'
1307
+ *
1308
+ * const obj2: UnknownRecord = {};
1309
+ * navigator.setValueByPath(obj2, 'address.city', 'Moscow');
1310
+ * // Создаст { address: { city: 'Moscow' } }
1311
+ *
1312
+ * const obj3 = { items: [{ title: 'Old' }] };
1313
+ * navigator.setValueByPath(obj3, 'items[0].title', 'New');
1314
+ * // obj3.items[0].title === 'New'
1315
+ * ```
1316
+ */
1317
+ setValueByPath(e, t, s) {
1318
+ const i = this.parsePath(t);
1319
+ if (i.length === 0)
1320
+ throw new Error("Cannot set value: empty path");
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];
1329
+ } else
1330
+ u == null && (r[l.key] = {}, u = r[l.key]), r = u;
1331
+ }
1332
+ const n = i[i.length - 1];
1333
+ if (n.index !== void 0) {
1334
+ const o = r[n.key];
1335
+ if (!Array.isArray(o))
1336
+ throw new Error(
1337
+ `Expected array at path segment: ${n.key}, but got ${typeof o}`
1338
+ );
1339
+ o[n.index] = s;
1340
+ } else
1341
+ r[n.key] = s;
1342
+ }
1343
+ /**
1344
+ * Получить значение из FormNode по пути
1345
+ *
1346
+ * Автоматически извлекает значение из FormNode (через .value.value).
1347
+ * Используется в ValidationContext и BehaviorContext для единообразного
1348
+ * доступа к значениям полей формы.
1349
+ *
1350
+ * @param form Корневой узел формы (обычно GroupNode)
1351
+ * @param path Путь к полю
1352
+ * @returns Значение поля или undefined, если путь не найден
1353
+ *
1354
+ * @example
1355
+ * ```typescript
1356
+ * const form = new GroupNode({
1357
+ * email: { value: 'test@mail.com', component: Input },
1358
+ * address: {
1359
+ * city: { value: 'Moscow', component: Input }
1360
+ * },
1361
+ * items: [{ title: { value: 'Item 1', component: Input } }]
1362
+ * });
1363
+ *
1364
+ * navigator.getFormNodeValue(form, 'email');
1365
+ * // 'test@mail.com'
1366
+ *
1367
+ * navigator.getFormNodeValue(form, 'address.city');
1368
+ * // 'Moscow'
1369
+ *
1370
+ * navigator.getFormNodeValue(form, 'items[0].title');
1371
+ * // 'Item 1'
1372
+ *
1373
+ * navigator.getFormNodeValue(form, 'invalid.path');
1374
+ * // undefined
1375
+ * ```
1376
+ */
1377
+ getFormNodeValue(e, t) {
1378
+ const s = this.getNodeByPath(e, t);
1379
+ if (s != null)
1380
+ return this.isFormNode(s) ? s.value.value : s;
1381
+ }
1382
+ /**
1383
+ * Type guard для проверки, является ли объект FormNode
1384
+ *
1385
+ * Проверяет наличие характерных свойств FormNode:
1386
+ * - value (Signal)
1387
+ * - value.value (значение Signal)
1388
+ *
1389
+ * @param obj Объект для проверки
1390
+ * @returns true, если объект является FormNode
1391
+ * @private
1392
+ */
1393
+ isFormNode(e) {
1394
+ return e != null && typeof e == "object" && "value" in e && typeof e.value == "object" && e.value != null && "value" in e.value;
1395
+ }
1396
+ /**
1397
+ * Получает узел формы по пути
1398
+ *
1399
+ * Навигирует по структуре FormNode (GroupNode/FieldNode/ArrayNode)
1400
+ * и возвращает узел по указанному пути.
1401
+ *
1402
+ * Поддерживает:
1403
+ * - Доступ к полям GroupNode через fields Map
1404
+ * - Доступ к элементам ArrayNode через индекс
1405
+ * - Proxy-доступ к полям (для обратной совместимости)
1406
+ *
1407
+ * @param form Корневой узел формы (обычно GroupNode)
1408
+ * @param path Путь к узлу
1409
+ * @returns Узел формы или null, если путь не найден
1410
+ *
1411
+ * @example
1412
+ * ```typescript
1413
+ * const form = new GroupNode({
1414
+ * email: { value: '', component: Input },
1415
+ * address: {
1416
+ * city: { value: '', component: Input }
1417
+ * },
1418
+ * items: [{ title: { value: '', component: Input } }]
1419
+ * });
1420
+ *
1421
+ * const emailNode = navigator.getNodeByPath(form, 'email');
1422
+ * // FieldNode
1423
+ *
1424
+ * const cityNode = navigator.getNodeByPath(form, 'address.city');
1425
+ * // FieldNode
1426
+ *
1427
+ * const itemNode = navigator.getNodeByPath(form, 'items[0]');
1428
+ * // GroupNode
1429
+ *
1430
+ * const titleNode = navigator.getNodeByPath(form, 'items[0].title');
1431
+ * // FieldNode
1432
+ *
1433
+ * const invalidNode = navigator.getNodeByPath(form, 'invalid.path');
1434
+ * // null
1435
+ * ```
1436
+ */
1437
+ getNodeByPath(e, t) {
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;
1443
+ if (n.fields && n.fields instanceof Map) {
1444
+ if (i = n.fields.get(r.key), r.index === void 0) {
1445
+ if (i == null) return null;
1446
+ continue;
1447
+ }
1448
+ } else if (r.index !== void 0 && n.items) {
1449
+ const o = n.items.value || n.items;
1450
+ if (!Array.isArray(o) || (i = o[r.index], i == null)) return null;
1451
+ continue;
1452
+ } else if (r.index === void 0) {
1453
+ if (i = n[r.key], i == null) return null;
1454
+ continue;
1455
+ }
1456
+ if (i && r.index !== void 0 && i.items) {
1457
+ const o = i.items.value || i.items;
1458
+ if (!Array.isArray(o)) return null;
1459
+ i = o[r.index];
1460
+ } else if (i && r.index !== void 0 && !i.items)
1461
+ return null;
1462
+ if (i == null) return null;
1463
+ }
1464
+ return i;
1465
+ }
1466
+ }
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
+ };
1483
+ }
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 {
1507
+ /**
1508
+ * @param form - Форма для отправки
1509
+ */
1510
+ constructor(e) {
1511
+ this.form = e;
1512
+ }
1513
+ /** Внутренний сигнал состояния отправки */
1514
+ _submitting = g(!1);
1515
+ /** Публичный read-only сигнал состояния отправки */
1516
+ submitting = d(() => this._submitting.value);
1517
+ /**
1518
+ * Отправить форму
1519
+ *
1520
+ * Процесс:
1521
+ * 1. Помечает все поля как touched (для отображения ошибок)
1522
+ * 2. Валидирует форму
1523
+ * 3. Если валидация успешна - вызывает onSubmit
1524
+ * 4. Управляет состоянием submitting
1525
+ *
1526
+ * @param onSubmit - Callback для отправки данных
1527
+ * @param options - Опции submit
1528
+ * @returns Результат от onSubmit или null если валидация не пройдена
1529
+ *
1530
+ * @example
1531
+ * ```typescript
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
+ * });
1539
+ *
1540
+ * if (result === null) {
1541
+ * console.log('Форма не прошла валидацию');
1542
+ * }
1543
+ * ```
1544
+ */
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
+ }
1555
+ }
1556
+ /**
1557
+ * Отправить форму с расширенным результатом
1558
+ *
1559
+ * В отличие от submit(), возвращает объект с информацией об успехе/ошибке
1560
+ *
1561
+ * @param onSubmit - Callback для отправки данных
1562
+ * @param options - Опции submit
1563
+ * @returns Объект SubmitResult с данными и статусом
1564
+ *
1565
+ * @example
1566
+ * ```typescript
1567
+ * const result = await submitter.submitWithResult(async (values) => {
1568
+ * return await api.saveForm(values);
1569
+ * });
1570
+ *
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
+ * }
1578
+ * ```
1579
+ */
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
+ }
1596
+ }
1597
+ /**
1598
+ * Проверить, идет ли отправка формы
1599
+ */
1600
+ isSubmitting() {
1601
+ return this._submitting.value;
1602
+ }
1603
+ }
1604
+ class k extends C {
1605
+ // ============================================================================
1606
+ // Приватные поля
1607
+ // ============================================================================
1608
+ id = crypto.randomUUID();
1609
+ /**
1610
+ * Коллекция полей формы (упрощённый Map вместо FieldRegistry)
1611
+ */
1612
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1613
+ _fields = /* @__PURE__ */ new Map();
1614
+ /**
1615
+ * Менеджер подписок для централизованного cleanup
1616
+ */
1617
+ disposers = new D();
1618
+ /**
1619
+ * Ссылка на Proxy-инстанс для использования в BehaviorContext
1620
+ */
1621
+ _proxyInstance;
1622
+ /**
1623
+ * Навигатор для работы с путями к полям
1624
+ */
1625
+ pathNavigator = new se();
1626
+ /**
1627
+ * Фабрика для создания узлов формы
1628
+ */
1629
+ nodeFactory = new ne();
1630
+ /**
1631
+ * Реестр валидаторов для этой формы
1632
+ * Может быть инжектирован через config._validationRegistry для тестирования
1633
+ */
1634
+ validationRegistry;
1635
+ /**
1636
+ * Реестр behaviors для этой формы
1637
+ * Может быть инжектирован через config._behaviorRegistry для тестирования
1638
+ */
1639
+ behaviorRegistry;
1640
+ /**
1641
+ * Аппликатор для применения валидаторов к форме
1642
+ */
1643
+ validationApplicator = new te(this);
1644
+ // ============================================================================
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
1656
+ // ============================================================================
1657
+ value;
1658
+ valid;
1659
+ invalid;
1660
+ touched;
1661
+ dirty;
1662
+ pending;
1663
+ errors;
1664
+ status;
1665
+ submitting;
1666
+ constructor(e) {
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);
1673
+ }
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);
1696
+ }
1697
+ // ============================================================================
1698
+ // Реализация абстрактных методов FormNode
1699
+ // ============================================================================
1700
+ getValue() {
1701
+ const e = {};
1702
+ return this._fields.forEach((t, s) => {
1703
+ e[s] = t.getValue();
1704
+ }), e;
1705
+ }
1706
+ setValue(e, t) {
1707
+ for (const [s, i] of Object.entries(e)) {
1708
+ const r = this._fields.get(s);
1709
+ r && r.setValue(i, t);
1710
+ }
1711
+ }
1712
+ patchValue(e) {
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
+ });
1719
+ }
1720
+ /**
1721
+ * Сбросить форму к указанным значениям (или к initialValues)
1722
+ *
1723
+ * @param value - опциональный объект со значениями для сброса
1724
+ *
1725
+ * @remarks
1726
+ * Рекурсивно вызывает reset() для всех полей формы
1727
+ *
1728
+ * @example
1729
+ * ```typescript
1730
+ * // Сброс к initialValues
1731
+ * form.reset();
1732
+ *
1733
+ * // Сброс к новым значениям
1734
+ * form.reset({ email: 'new@mail.com', password: '' });
1735
+ * ```
1736
+ */
1737
+ reset(e) {
1738
+ this._fields.forEach((t, s) => {
1739
+ const i = e?.[s];
1740
+ t.reset(i);
1741
+ });
1742
+ }
1743
+ /**
1744
+ * Сбросить форму к исходным значениям (initialValues)
1745
+ */
1746
+ resetToInitial() {
1747
+ this._fields.forEach((e) => {
1748
+ "resetToInitial" in e && typeof e.resetToInitial == "function" ? e.resetToInitial() : e.reset();
1749
+ });
1750
+ }
1751
+ async validate() {
1752
+ this.clearErrors(), await Promise.all(Array.from(this._fields.values()).map((t) => t.validate()));
1753
+ const e = this.validationRegistry.getValidators();
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
+ );
1757
+ }
1758
+ /**
1759
+ * Установить form-level validation errors
1760
+ */
1761
+ setErrors(e) {
1762
+ this._formErrors.value = e;
1763
+ }
1764
+ /**
1765
+ * Очистить все errors (form-level + field-level)
1766
+ */
1767
+ clearErrors() {
1768
+ this._formErrors.value = [], this._fields.forEach((e) => e.clearErrors());
1769
+ }
1770
+ /**
1771
+ * Получить поле по ключу
1772
+ */
1773
+ getField(e) {
1774
+ return this._fields.get(e);
1775
+ }
1776
+ /**
1777
+ * Получить Map всех полей формы (для совместимости)
1778
+ */
1779
+ get fields() {
1780
+ return this._fields;
1781
+ }
1782
+ /**
1783
+ * Получить Proxy-инстанс для прямого доступа к полям
1784
+ *
1785
+ * Proxy позволяет обращаться к полям формы напрямую через точечную нотацию:
1786
+ * - form.email вместо form.fields.get('email')
1787
+ * - form.address.city вместо form.fields.get('address').fields.get('city')
1788
+ *
1789
+ * Используется в:
1790
+ * - BehaviorApplicator для доступа к полям в behavior functions
1791
+ * - ValidationApplicator для доступа к форме в tree validators
1792
+ *
1793
+ * @returns Proxy-инстанс с типобезопасным доступом к полям или сама форма, если proxy не доступен
1794
+ *
1795
+ * @example
1796
+ * ```typescript
1797
+ * const form = new GroupNode({
1798
+ * controls: {
1799
+ * email: new FieldNode({ value: '' }),
1800
+ * name: new FieldNode({ value: '' })
1801
+ * }
1802
+ * });
1803
+ *
1804
+ * const proxy = form.getProxy();
1805
+ * console.log(proxy.email.value); // Прямой доступ к полю
1806
+ * ```
1807
+ */
1808
+ getProxy() {
1809
+ return this._proxyInstance || (this._proxyInstance = this.buildProxy()), this._proxyInstance;
1810
+ }
1811
+ /**
1812
+ * Получить все поля формы как итератор
1813
+ */
1814
+ getAllFields() {
1815
+ return this._fields.values();
1816
+ }
1817
+ // ============================================================================
1818
+ // Protected hooks (Template Method pattern)
1819
+ // ============================================================================
1820
+ onMarkAsTouched() {
1821
+ this._fields.forEach((e) => e.markAsTouched());
1822
+ }
1823
+ onMarkAsUntouched() {
1824
+ this._fields.forEach((e) => e.markAsUntouched());
1825
+ }
1826
+ onMarkAsDirty() {
1827
+ this._fields.forEach((e) => e.markAsDirty());
1828
+ }
1829
+ onMarkAsPristine() {
1830
+ this._fields.forEach((e) => e.markAsPristine());
1831
+ }
1832
+ // ============================================================================
1833
+ // Дополнительные методы (из FormStore)
1834
+ // ============================================================================
1835
+ /**
1836
+ * Отправить форму
1837
+ *
1838
+ * @param onSubmit - Callback для отправки данных
1839
+ * @param options - Опции submit (skipValidation, skipTouch)
1840
+ * @returns Результат от onSubmit или null если валидация не пройдена
1841
+ */
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);
1854
+ }
1855
+ /**
1856
+ * Применить validation schema к форме
1857
+ *
1858
+ * Использует локальный реестр валидаторов (this.validationRegistry)
1859
+ * вместо глобального Singleton для изоляции форм друг от друга.
1860
+ */
1861
+ applyValidationSchema(e) {
1862
+ this.validationRegistry.beginRegistration();
1863
+ try {
1864
+ const t = I();
1865
+ e(t);
1866
+ const s = this.getProxy();
1867
+ this.validationRegistry.endRegistration(s);
1868
+ } catch (t) {
1869
+ throw console.error("Error applying validation schema:", t), t;
1870
+ }
1871
+ }
1872
+ /**
1873
+ * Применить behavior schema к форме
1874
+ * @returns Функция cleanup для отписки от всех behaviors
1875
+ */
1876
+ applyBehaviorSchema(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
+ }
1884
+ }
1885
+ /**
1886
+ * Получить вложенное поле по пути
1887
+ *
1888
+ * Поддерживаемые форматы путей:
1889
+ * - Simple: "email" - получить поле верхнего уровня
1890
+ * - Nested: "address.city" - получить вложенное поле
1891
+ * - Array index: "items[0]" - получить элемент массива по индексу
1892
+ * - Combined: "items[0].name" - получить поле элемента массива
1893
+ *
1894
+ * @param path - Путь к полю
1895
+ * @returns FormNode если найдено, undefined если путь не существует
1896
+ *
1897
+ * @example
1898
+ * ```typescript
1899
+ * const form = new GroupNode({
1900
+ * email: { value: '', component: Input },
1901
+ * address: {
1902
+ * city: { value: '', component: Input }
1903
+ * },
1904
+ * items: [{ name: { value: '', component: Input } }]
1905
+ * });
1906
+ *
1907
+ * form.getFieldByPath('email'); // FieldNode
1908
+ * form.getFieldByPath('address.city'); // FieldNode
1909
+ * form.getFieldByPath('items[0]'); // GroupNode
1910
+ * form.getFieldByPath('items[0].name'); // FieldNode
1911
+ * form.getFieldByPath('invalid.path'); // undefined
1912
+ * ```
1913
+ */
1914
+ getFieldByPath(e) {
1915
+ if (e.startsWith(".") || e.endsWith("."))
1916
+ return;
1917
+ const t = this.pathNavigator.parsePath(e);
1918
+ if (t.length === 0)
1919
+ return;
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;
1928
+ } else
1929
+ return;
1930
+ }
1931
+ return s;
1932
+ }
1933
+ /**
1934
+ * Применить contextual валидаторы к полям
1935
+ *
1936
+ * ✅ РЕФАКТОРИНГ: Делегирование ValidationApplicator (SRP)
1937
+ *
1938
+ * Логика применения валидаторов извлечена в ValidationApplicator для:
1939
+ * - Соблюдения Single Responsibility Principle
1940
+ * - Уменьшения размера GroupNode (~120 строк)
1941
+ * - Улучшения тестируемости
1942
+ *
1943
+ * @param validators Зарегистрированные валидаторы
1944
+ */
1945
+ async applyContextualValidators(e) {
1946
+ await this.validationApplicator.apply(e);
1947
+ }
1948
+ // ============================================================================
1949
+ // Private методы для создания узлов
1950
+ // ============================================================================
1951
+ /**
1952
+ * Создать узел на основе конфигурации
1953
+ *
1954
+ * ✅ РЕФАКТОРИНГ: Полное делегирование NodeFactory
1955
+ *
1956
+ * NodeFactory теперь обрабатывает:
1957
+ * - Массивы [schema, ...items]
1958
+ * - FieldConfig
1959
+ * - GroupConfig
1960
+ * - ArrayConfig
1961
+ *
1962
+ * @param config Конфигурация узла
1963
+ * @returns Созданный узел формы
1964
+ * @private
1965
+ */
1966
+ createNode(e) {
1967
+ return this.nodeFactory.createNode(e);
1968
+ }
1969
+ // ============================================================================
1970
+ // Методы-помощники для реактивности (Фаза 1)
1971
+ // ============================================================================
1972
+ /**
1973
+ * Связывает два поля: при изменении source автоматически обновляется target
1974
+ */
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);
1985
+ return this.disposers.add(o, n);
1986
+ }
1987
+ /**
1988
+ * Подписка на изменения вложенного поля по строковому пути
1989
+ * Поддерживает вложенные пути типа "address.city"
1990
+ *
1991
+ * @param fieldPath - Строковый путь к полю (например, "address.city")
1992
+ * @param callback - Функция, вызываемая при изменении поля
1993
+ * @returns Функция отписки для cleanup
1994
+ *
1995
+ * @example
1996
+ * ```typescript
1997
+ * // Подписка на изменение страны для загрузки городов
1998
+ * const dispose = form.watchField(
1999
+ * 'registrationAddress.country',
2000
+ * async (countryCode) => {
2001
+ * if (countryCode) {
2002
+ * const cities = await fetchCitiesByCountry(countryCode);
2003
+ * form.registrationAddress.city.updateComponentProps({
2004
+ * options: cities
2005
+ * });
2006
+ * }
2007
+ * }
2008
+ * );
2009
+ *
2010
+ * // Cleanup
2011
+ * useEffect(() => dispose, []);
2012
+ * ```
2013
+ */
2014
+ watchField(e, t) {
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;
2020
+ t(n);
2021
+ }), r = A(w.WatchField);
2022
+ return this.disposers.add(r, i);
2023
+ }
2024
+ /**
2025
+ * Hook: вызывается после disable()
2026
+ */
2027
+ onDisable() {
2028
+ this._disabled.value = !0, this._fields.forEach((e) => e.disable());
2029
+ }
2030
+ /**
2031
+ * Hook: вызывается после enable()
2032
+ */
2033
+ onEnable() {
2034
+ this._disabled.value = !1, this._fields.forEach((e) => e.enable());
2035
+ }
2036
+ /**
2037
+ * Очистить все ресурсы узла
2038
+ */
2039
+ dispose() {
2040
+ this.disposers.dispose(), this._fields.forEach((e) => {
2041
+ "dispose" in e && typeof e.dispose == "function" && e.dispose();
2042
+ });
2043
+ }
2044
+ }
2045
+ class L extends C {
2046
+ // ============================================================================
2047
+ // Приватные поля
2048
+ // ============================================================================
2049
+ items;
2050
+ itemSchema;
2051
+ initialItems;
2052
+ /**
2053
+ * Менеджер подписок для централизованного cleanup
2054
+ * Использует SubscriptionManager вместо массива для управления подписками
2055
+ */
2056
+ disposers = new D();
2057
+ /** Array-level validation errors (e.g., "минимум 1 элемент") */
2058
+ _arrayErrors = g([]);
2059
+ // ============================================================================
2060
+ // Приватные поля для сохранения схем
2061
+ // ============================================================================
2062
+ validationSchemaFn;
2063
+ behaviorSchemaFn;
2064
+ // ============================================================================
2065
+ // Публичные computed signals
2066
+ // ============================================================================
2067
+ value;
2068
+ valid;
2069
+ invalid;
2070
+ touched;
2071
+ dirty;
2072
+ pending;
2073
+ errors;
2074
+ status;
2075
+ length;
2076
+ // ============================================================================
2077
+ // Конструктор
2078
+ // ============================================================================
2079
+ constructor(e, t = []) {
2080
+ super(), this.itemSchema = e, this.initialItems = t, this.items = g([]);
2081
+ for (const i of t)
2082
+ this.push(i);
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;
2089
+ }
2090
+ // ============================================================================
2091
+ // CRUD операции
2092
+ // ============================================================================
2093
+ /**
2094
+ * Добавить элемент в конец массива
2095
+ * @param initialValue - Начальные значения для нового элемента
2096
+ */
2097
+ push(e) {
2098
+ const t = this.createItem(e);
2099
+ this.items.value = [...this.items.value, t];
2100
+ }
2101
+ /**
2102
+ * Удалить элемент по индексу
2103
+ * @param index - Индекс элемента для удаления
2104
+ *
2105
+ * @remarks
2106
+ * Вызывает dispose() на удаляемом элементе для очистки подписок
2107
+ */
2108
+ removeAt(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();
2121
+ }
2122
+ /**
2123
+ * Вставить элемент в массив
2124
+ * @param index - Индекс для вставки
2125
+ * @param initialValue - Начальные значения для нового элемента
2126
+ */
2127
+ insert(e, t) {
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
+ );
2136
+ return;
2137
+ }
2138
+ const s = this.createItem(t), i = [...this.items.value];
2139
+ i.splice(e, 0, s), this.items.value = i;
2140
+ }
2141
+ /**
2142
+ * Удалить все элементы массива
2143
+ *
2144
+ * @remarks
2145
+ * Вызывает dispose() на всех элементах для очистки подписок
2146
+ */
2147
+ clear() {
2148
+ const e = [...this.items.value];
2149
+ this.items.value = [], e.forEach((t) => {
2150
+ "dispose" in t && typeof t.dispose == "function" && t.dispose();
2151
+ });
2152
+ }
2153
+ /**
2154
+ * Получить элемент по индексу
2155
+ * @param index - Индекс элемента
2156
+ * @returns Типизированный GroupNode proxy или undefined если индекс вне границ
2157
+ */
2158
+ at(e) {
2159
+ const t = this.items.value[e];
2160
+ if (t)
2161
+ return t.getProxy();
2162
+ }
2163
+ // ============================================================================
2164
+ // Реализация абстрактных методов
2165
+ // ============================================================================
2166
+ getValue() {
2167
+ return this.items.value.map((e) => e.getValue());
2168
+ }
2169
+ setValue(e, t) {
2170
+ this.clear(), e.forEach((s) => this.push(s)), t?.emitEvent !== !1 && this.validate().catch((s) => {
2171
+ S.handle(s, "ArrayNode.setValue", V.LOG);
2172
+ });
2173
+ }
2174
+ patchValue(e) {
2175
+ e.forEach((t, s) => {
2176
+ this.items.value[s] && t !== void 0 && this.items.value[s].patchValue(t);
2177
+ });
2178
+ }
2179
+ /**
2180
+ * Сбросить массив к указанным значениям (или очистить)
2181
+ *
2182
+ * @param values - опциональный массив значений для сброса
2183
+ *
2184
+ * @remarks
2185
+ * Очищает текущий массив и заполняет новыми элементами
2186
+ *
2187
+ * @example
2188
+ * ```typescript
2189
+ * // Очистить массив
2190
+ * arrayNode.reset();
2191
+ *
2192
+ * // Сбросить к новым значениям
2193
+ * arrayNode.reset([{ name: 'Item 1' }, { name: 'Item 2' }]);
2194
+ * ```
2195
+ */
2196
+ reset(e) {
2197
+ this._arrayErrors.value = [], this.clear(), e && e.forEach((t) => this.push(t));
2198
+ }
2199
+ /**
2200
+ * Сбросить массив к исходным значениям (initialItems)
2201
+ *
2202
+ * @remarks
2203
+ * Восстанавливает массив в состояние, которое было при создании ArrayNode.
2204
+ * Более явный способ сброса к начальным значениям по сравнению с reset()
2205
+ *
2206
+ * Полезно когда:
2207
+ * - Пользователь нажал "Cancel" - вернуть массив к исходным элементам
2208
+ * - Массив был изменен через reset(newValues), но нужно вернуться к началу
2209
+ * - Явное намерение показать "отмена всех изменений"
2210
+ *
2211
+ * @example
2212
+ * ```typescript
2213
+ * const arrayNode = new ArrayNode(
2214
+ * { name: { value: '', component: Input } },
2215
+ * [{ name: 'Initial 1' }, { name: 'Initial 2' }]
2216
+ * );
2217
+ *
2218
+ * arrayNode.push({ name: 'New Item' });
2219
+ * arrayNode.reset([{ name: 'Temp' }]);
2220
+ * console.log(arrayNode.length.value); // 1
2221
+ *
2222
+ * arrayNode.resetToInitial();
2223
+ * console.log(arrayNode.length.value); // 2
2224
+ * console.log(arrayNode.at(0)?.name.value.value); // 'Initial 1'
2225
+ * ```
2226
+ */
2227
+ resetToInitial() {
2228
+ this._arrayErrors.value = [], this.clear(), this.initialItems.forEach((e) => this.push(e));
2229
+ }
2230
+ async validate() {
2231
+ return (await Promise.all(this.items.value.map((t) => t.validate()))).every(Boolean);
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
+ */
2246
+ setErrors(e) {
2247
+ this._arrayErrors.value = e;
2248
+ }
2249
+ /**
2250
+ * Очистить все errors (array-level + item-level)
2251
+ *
2252
+ * @example
2253
+ * ```typescript
2254
+ * arrayNode.clearErrors();
2255
+ * console.log(arrayNode.errors.value); // []
2256
+ * ```
2257
+ */
2258
+ clearErrors() {
2259
+ this._arrayErrors.value = [], this.items.value.forEach((e) => e.clearErrors());
2260
+ }
2261
+ // ============================================================================
2262
+ // Protected hooks (Template Method pattern)
2263
+ // ============================================================================
2264
+ /**
2265
+ * Hook: вызывается после markAsTouched()
2266
+ *
2267
+ * Для ArrayNode: рекурсивно помечаем все элементы массива как touched
2268
+ */
2269
+ onMarkAsTouched() {
2270
+ this.items.value.forEach((e) => e.markAsTouched());
2271
+ }
2272
+ /**
2273
+ * Hook: вызывается после markAsUntouched()
2274
+ *
2275
+ * Для ArrayNode: рекурсивно помечаем все элементы массива как untouched
2276
+ */
2277
+ onMarkAsUntouched() {
2278
+ this.items.value.forEach((e) => e.markAsUntouched());
2279
+ }
2280
+ /**
2281
+ * Hook: вызывается после markAsDirty()
2282
+ *
2283
+ * Для ArrayNode: рекурсивно помечаем все элементы массива как dirty
2284
+ */
2285
+ onMarkAsDirty() {
2286
+ this.items.value.forEach((e) => e.markAsDirty());
2287
+ }
2288
+ /**
2289
+ * Hook: вызывается после markAsPristine()
2290
+ *
2291
+ * Для ArrayNode: рекурсивно помечаем все элементы массива как pristine
2292
+ */
2293
+ onMarkAsPristine() {
2294
+ this.items.value.forEach((e) => e.markAsPristine());
2295
+ }
2296
+ // ============================================================================
2297
+ // Итерация
2298
+ // ============================================================================
2299
+ /**
2300
+ * Итерировать по элементам массива
2301
+ * @param callback - Функция, вызываемая для каждого элемента с типизированным GroupNode proxy
2302
+ */
2303
+ forEach(e) {
2304
+ this.items.value.forEach((t, s) => {
2305
+ const i = t.getProxy();
2306
+ e(i, s);
2307
+ });
2308
+ }
2309
+ /**
2310
+ * Маппинг элементов массива
2311
+ * @param callback - Функция преобразования с типизированным GroupNode proxy
2312
+ * @returns Новый массив результатов
2313
+ */
2314
+ map(e) {
2315
+ return this.items.value.map((t, s) => {
2316
+ const i = t.getProxy();
2317
+ return e(i, s);
2318
+ });
2319
+ }
2320
+ // ============================================================================
2321
+ // Private методы
2322
+ // ============================================================================
2323
+ /**
2324
+ * Создать новый элемент массива на основе схемы
2325
+ * @param initialValue - Начальные значения
2326
+ */
2327
+ createItem(e) {
2328
+ if (this.isGroupSchema(this.itemSchema)) {
2329
+ const t = new k(this.itemSchema);
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;
2331
+ }
2332
+ throw new Error(
2333
+ "ArrayNode поддерживает только GroupNode элементы. Для массива примитивов используйте обычное поле с типом массива."
2334
+ );
2335
+ }
2336
+ /**
2337
+ * Проверить, является ли схема групповой (объект полей)
2338
+ * @param schema - Схема для проверки
2339
+ */
2340
+ isGroupSchema(e) {
2341
+ return typeof e == "object" && e !== null && !("component" in e) && !Array.isArray(e);
2342
+ }
2343
+ // ============================================================================
2344
+ // Validation Schema
2345
+ // ============================================================================
2346
+ /**
2347
+ * Применить validation schema ко всем элементам массива
2348
+ *
2349
+ * Validation schema будет применена к:
2350
+ * - Всем существующим элементам
2351
+ * - Всем новым элементам, добавляемым через push/insert
2352
+ *
2353
+ * @param schemaFn - Функция валидации для элемента массива
2354
+ *
2355
+ * @example
2356
+ * ```typescript
2357
+ * import { propertyValidation } from './validation/property-validation';
2358
+ *
2359
+ * form.properties.applyValidationSchema(propertyValidation);
2360
+ * ```
2361
+ */
2362
+ applyValidationSchema(e) {
2363
+ this.validationSchemaFn = e, this.items.value.forEach((t) => {
2364
+ "applyValidationSchema" in t && typeof t.applyValidationSchema == "function" && t.applyValidationSchema(e);
2365
+ });
2366
+ }
2367
+ /**
2368
+ * Применить behavior schema ко всем элементам ArrayNode
2369
+ *
2370
+ * Автоматически применяется к новым элементам при push/insert.
2371
+ *
2372
+ * @param schemaFn - Behavior schema функция
2373
+ *
2374
+ * @example
2375
+ * ```typescript
2376
+ * import { addressBehavior } from './behaviors/address-behavior';
2377
+ *
2378
+ * form.addresses.applyBehaviorSchema(addressBehavior);
2379
+ * ```
2380
+ */
2381
+ applyBehaviorSchema(e) {
2382
+ this.behaviorSchemaFn = e, this.items.value.forEach((t) => {
2383
+ "applyBehaviorSchema" in t && typeof t.applyBehaviorSchema == "function" && t.applyBehaviorSchema(e);
2384
+ });
2385
+ }
2386
+ // ============================================================================
2387
+ // Методы-помощники для реактивности (Фаза 1)
2388
+ // ============================================================================
2389
+ /**
2390
+ * Подписка на изменения конкретного поля во всех элементах массива
2391
+ * Срабатывает при изменении значения поля в любом элементе
2392
+ *
2393
+ * @param fieldKey - Ключ поля для отслеживания
2394
+ * @param callback - Функция, вызываемая при изменении, получает массив всех значений и индекс измененного элемента
2395
+ * @returns Функция отписки для cleanup
2396
+ *
2397
+ * @example
2398
+ * ```typescript
2399
+ * // Автоматический пересчет общей стоимости при изменении цен
2400
+ * const dispose = form.existingLoans.watchItems(
2401
+ * 'remainingAmount',
2402
+ * (amounts) => {
2403
+ * const totalDebt = amounts.reduce((sum, amount) => sum + (amount || 0), 0);
2404
+ * form.totalDebt.setValue(totalDebt);
2405
+ * }
2406
+ * );
2407
+ *
2408
+ * // При изменении любого remainingAmount → пересчитается totalDebt
2409
+ * form.existingLoans.at(0)?.remainingAmount.setValue(500000);
2410
+ *
2411
+ * // Cleanup
2412
+ * useEffect(() => dispose, []);
2413
+ * ```
2414
+ */
2415
+ watchItems(e, t) {
2416
+ const s = m(() => {
2417
+ const r = this.items.value.map((n) => {
2418
+ if (n instanceof k)
2419
+ return n.getFieldByPath(e)?.value.value;
2420
+ });
2421
+ t(r);
2422
+ }), i = A(w.WatchItems);
2423
+ return this.disposers.add(i, s);
2424
+ }
2425
+ /**
2426
+ * Подписка на изменение длины массива
2427
+ * Срабатывает при добавлении/удалении элементов
2428
+ *
2429
+ * @param callback - Функция, вызываемая при изменении длины, получает новую длину
2430
+ * @returns Функция отписки для cleanup
2431
+ *
2432
+ * @example
2433
+ * ```typescript
2434
+ * // Обновление счетчика элементов в UI
2435
+ * const dispose = form.properties.watchLength((length) => {
2436
+ * console.log(`Количество объектов недвижимости: ${length}`);
2437
+ * form.propertyCount.setValue(length);
2438
+ * });
2439
+ *
2440
+ * form.properties.push({ title: 'Квартира', value: 5000000 });
2441
+ * // Выведет: "Количество объектов недвижимости: 1"
2442
+ *
2443
+ * // Cleanup
2444
+ * useEffect(() => dispose, []);
2445
+ * ```
2446
+ */
2447
+ watchLength(e) {
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);
2453
+ }
2454
+ /**
2455
+ * Очистить все ресурсы узла
2456
+ * Рекурсивно очищает все subscriptions и элементы массива
2457
+ *
2458
+ * @example
2459
+ * ```typescript
2460
+ * useEffect(() => {
2461
+ * return () => {
2462
+ * arrayNode.dispose();
2463
+ * };
2464
+ * }, []);
2465
+ * ```
2466
+ */
2467
+ dispose() {
2468
+ this.disposers.dispose(), this.items.value.forEach((e) => {
2469
+ "dispose" in e && typeof e.dispose == "function" && e.dispose();
2470
+ });
2471
+ }
2472
+ /**
2473
+ * Hook: вызывается после disable()
2474
+ *
2475
+ * Для ArrayNode: рекурсивно отключаем все элементы массива
2476
+ *
2477
+ * @example
2478
+ * ```typescript
2479
+ * // Отключить весь массив полей
2480
+ * form.items.disable();
2481
+ *
2482
+ * // Все элементы становятся disabled
2483
+ * form.items.forEach(item => {
2484
+ * console.log(item.status.value); // 'disabled'
2485
+ * });
2486
+ * ```
2487
+ */
2488
+ onDisable() {
2489
+ this.items.value.forEach((e) => {
2490
+ e.disable();
2491
+ });
2492
+ }
2493
+ /**
2494
+ * Hook: вызывается после enable()
2495
+ *
2496
+ * Для ArrayNode: рекурсивно включаем все элементы массива
2497
+ *
2498
+ * @example
2499
+ * ```typescript
2500
+ * // Включить весь массив полей
2501
+ * form.items.enable();
2502
+ *
2503
+ * // Все элементы становятся enabled
2504
+ * form.items.forEach(item => {
2505
+ * console.log(item.status.value); // 'valid' или 'invalid'
2506
+ * });
2507
+ * ```
2508
+ */
2509
+ onEnable() {
2510
+ this.items.value.forEach((e) => {
2511
+ e.enable();
2512
+ });
2513
+ }
2514
+ }
2515
+ class ne {
2516
+ /**
2517
+ * Создает узел формы на основе конфигурации
2518
+ *
2519
+ * ✅ ОБНОВЛЕНО: Теперь поддерживает массивы напрямую
2520
+ *
2521
+ * Автоматически определяет тип узла:
2522
+ * - FieldNode: имеет value и component
2523
+ * - ArrayNode: массив [schema, ...items] или { schema, initialItems }
2524
+ * - GroupNode: объект без value, component, schema
2525
+ *
2526
+ * @param config Конфигурация узла
2527
+ * @returns Экземпляр FieldNode, GroupNode или ArrayNode
2528
+ * @throws Error если конфиг не соответствует ни одному типу
2529
+ *
2530
+ * @example
2531
+ * ```typescript
2532
+ * const factory = new NodeFactory();
2533
+ *
2534
+ * // FieldNode
2535
+ * const field = factory.createNode({
2536
+ * value: 'test@mail.com',
2537
+ * component: Input,
2538
+ * validators: [required, email]
2539
+ * });
2540
+ *
2541
+ * // GroupNode
2542
+ * const group = factory.createNode({
2543
+ * email: { value: '', component: Input },
2544
+ * password: { value: '', component: Input }
2545
+ * });
2546
+ *
2547
+ * // ArrayNode (объект)
2548
+ * const array = factory.createNode({
2549
+ * schema: { title: { value: '', component: Input } },
2550
+ * initialItems: [{ title: 'Item 1' }]
2551
+ * });
2552
+ *
2553
+ * // ArrayNode (массив) - новый формат
2554
+ * const array2 = factory.createNode([
2555
+ * { title: { value: '', component: Input } }, // schema
2556
+ * { title: 'Item 1' }, // initial item 1
2557
+ * { title: 'Item 2' } // initial item 2
2558
+ * ]);
2559
+ * ```
2560
+ */
2561
+ createNode(e) {
2562
+ if (Array.isArray(e) && e.length >= 1)
2563
+ return this.createArrayNodeFromArray(e);
2564
+ if (this.isFieldConfig(e))
2565
+ return new ee(e);
2566
+ if (this.isArrayConfig(e)) {
2567
+ const t = e;
2568
+ return new L(
2569
+ t.schema,
2570
+ t.initialItems
2571
+ );
2572
+ }
2573
+ if (this.isGroupConfig(e))
2574
+ return new k(e);
2575
+ throw new Error(
2576
+ `NodeFactory: Unknown node config. Expected FieldConfig, GroupConfig, or ArrayConfig, but got: ${JSON.stringify(
2577
+ e
2578
+ )}`
2579
+ );
2580
+ }
2581
+ /**
2582
+ * Создать ArrayNode из массива [schema, ...initialItems]
2583
+ *
2584
+ * ✅ НОВОЕ: Извлечено из GroupNode для централизации логики
2585
+ *
2586
+ * Формат: [itemSchema, ...initialItems]
2587
+ * - Первый элемент - схема элемента массива
2588
+ * - Остальные элементы - начальные значения
2589
+ *
2590
+ * @param config Массив с схемой и начальными элементами
2591
+ * @returns ArrayNode
2592
+ *
2593
+ * @example
2594
+ * ```typescript
2595
+ * const factory = new NodeFactory();
2596
+ *
2597
+ * // Массив с начальными элементами
2598
+ * const array = factory.createArrayNodeFromArray([
2599
+ * { title: { value: '', component: Input } }, // schema
2600
+ * { title: 'Item 1' }, // initial value
2601
+ * { title: 'Item 2' } // initial value
2602
+ * ]);
2603
+ * ```
2604
+ * @private
2605
+ */
2606
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2607
+ createArrayNodeFromArray(e) {
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);
2613
+ }
2614
+ /**
2615
+ * Извлечь значения из схемы (рекурсивно)
2616
+ *
2617
+ * ✅ НОВОЕ: Извлечено из GroupNode для централизации логики
2618
+ *
2619
+ * Преобразует схему формы в объект со значениями:
2620
+ * - `{ name: { value: 'John', component: Input } } → { name: 'John' }`
2621
+ * - Поддерживает вложенные группы
2622
+ * - Поддерживает массивы
2623
+ *
2624
+ * @param schema Схема формы
2625
+ * @returns Объект со значениями полей
2626
+ *
2627
+ * @example
2628
+ * ```typescript
2629
+ * const factory = new NodeFactory();
2630
+ *
2631
+ * const schema = {
2632
+ * name: { value: 'John', component: Input },
2633
+ * age: { value: 30, component: Input },
2634
+ * address: {
2635
+ * city: { value: 'Moscow', component: Input }
2636
+ * }
2637
+ * };
2638
+ *
2639
+ * factory.extractValues(schema);
2640
+ * // { name: 'John', age: 30, address: { city: 'Moscow' } }
2641
+ * ```
2642
+ */
2643
+ extractValues(e) {
2644
+ if (this.isFieldConfig(e))
2645
+ return e.value;
2646
+ if (Array.isArray(e))
2647
+ return e.map((t) => this.extractValues(t));
2648
+ if (this.isGroupConfig(e)) {
2649
+ const t = {};
2650
+ for (const [s, i] of Object.entries(e))
2651
+ t[s] = this.extractValues(i);
2652
+ return t;
2653
+ }
2654
+ return e;
2655
+ }
2656
+ /**
2657
+ * Проверяет, является ли конфиг конфигурацией поля (FieldConfig)
2658
+ *
2659
+ * FieldConfig имеет обязательные свойства:
2660
+ * - value: начальное значение поля
2661
+ * - component: React-компонент для отображения
2662
+ *
2663
+ * @param config Проверяемая конфигурация
2664
+ * @returns true если config является FieldConfig
2665
+ *
2666
+ * @example
2667
+ * ```typescript
2668
+ * const factory = new NodeFactory();
2669
+ *
2670
+ * factory.isFieldConfig({ value: '', component: Input }); // true
2671
+ * factory.isFieldConfig({ email: { value: '' } }); // false
2672
+ * factory.isFieldConfig(null); // false
2673
+ * ```
2674
+ */
2675
+ isFieldConfig(e) {
2676
+ return e != null && typeof e == "object" && "value" in e && "component" in e;
2677
+ }
2678
+ /**
2679
+ * Проверяет, является ли конфиг конфигурацией массива (ArrayConfig)
2680
+ *
2681
+ * ArrayConfig имеет обязательное свойство:
2682
+ * - schema: схема для элементов массива
2683
+ *
2684
+ * И НЕ имеет:
2685
+ * - value (отличие от FieldConfig)
2686
+ *
2687
+ * @param config Проверяемая конфигурация
2688
+ * @returns true если config является ArrayConfig
2689
+ *
2690
+ * @example
2691
+ * ```typescript
2692
+ * const factory = new NodeFactory();
2693
+ *
2694
+ * factory.isArrayConfig({ schema: {}, initialItems: [] }); // true
2695
+ * factory.isArrayConfig({ value: '', component: Input }); // false
2696
+ * factory.isArrayConfig({ email: { value: '' } }); // false
2697
+ * ```
2698
+ */
2699
+ isArrayConfig(e) {
2700
+ return e != null && typeof e == "object" && "schema" in e && !("value" in e);
2701
+ }
2702
+ /**
2703
+ * Проверяет, является ли конфиг конфигурацией группы (GroupConfig)
2704
+ *
2705
+ * GroupConfig - это объект, который:
2706
+ * - НЕ является FieldConfig (нет value/component)
2707
+ * - НЕ является ArrayConfig (нет schema)
2708
+ * - Содержит вложенные конфиги полей/групп/массивов
2709
+ *
2710
+ * @param config Проверяемая конфигурация
2711
+ * @returns true если config является GroupConfig
2712
+ *
2713
+ * @example
2714
+ * ```typescript
2715
+ * const factory = new NodeFactory();
2716
+ *
2717
+ * factory.isGroupConfig({
2718
+ * email: { value: '', component: Input },
2719
+ * password: { value: '', component: Input }
2720
+ * }); // true
2721
+ *
2722
+ * factory.isGroupConfig({ value: '', component: Input }); // false
2723
+ * factory.isGroupConfig({ schema: {} }); // false
2724
+ * factory.isGroupConfig(null); // false
2725
+ * ```
2726
+ */
2727
+ isGroupConfig(e) {
2728
+ return e != null && typeof e == "object" && !this.isFieldConfig(e) && !this.isArrayConfig(e);
2729
+ }
2730
+ }
2731
+ function ge(a) {
2732
+ return new k(a).getProxy();
2733
+ }
2734
+ class be {
2735
+ /**
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
+ * Подписаться на события изменения
2752
+ *
2753
+ * @param callback - Функция обработки события
2754
+ * @returns Функция отписки
2755
+ *
2756
+ * @example
2757
+ * ```typescript
2758
+ * const unsubscribe = observer.subscribe((event) => {
2759
+ * // Отправить событие в analytics
2760
+ * analytics.track('form_change', event);
2761
+ * });
2762
+ * ```
2763
+ */
2764
+ subscribe(e) {
2765
+ return this.listeners.add(e), () => this.listeners.delete(e);
2766
+ }
2767
+ /**
2768
+ * Включить трассировку формы
2769
+ *
2770
+ * Подписывается на изменения основных сигналов формы
2771
+ * и вызывает listeners при каждом изменении
2772
+ *
2773
+ * @returns Функция для отключения трассировки
2774
+ *
2775
+ * @example
2776
+ * ```typescript
2777
+ * const dispose = observer.enableTracing();
2778
+ *
2779
+ * // Позже, для отключения
2780
+ * dispose();
2781
+ * ```
2782
+ */
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
+ };
2857
+ }
2858
+ /**
2859
+ * Наблюдать за конкретным полем
2860
+ *
2861
+ * @param path - Путь к полю
2862
+ * @returns Функция для отключения наблюдения
2863
+ *
2864
+ * @example
2865
+ * ```typescript
2866
+ * // Наблюдать за полем email
2867
+ * const dispose = observer.watchField('email');
2868
+ * ```
2869
+ */
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());
2905
+ }
2906
+ /**
2907
+ * Отправить событие всем listeners
2908
+ */
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
+ }));
2916
+ }
2917
+ /**
2918
+ * Проверить, нужно ли отслеживать тип события
2919
+ */
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();
2935
+ }
2936
+ }
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;
2978
+ }
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);
2996
+ }
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;
3014
+ }
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
+ }
3024
+ }
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;
3032
+ }
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;
3043
+ return !0;
3044
+ }
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;
3061
+ return;
3062
+ }
3063
+ n();
3064
+ });
3065
+ },
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;
3078
+ }
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);
3091
+ }
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);
3162
+ }
3163
+ function Ee(a) {
3164
+ const e = a && "length" in a && "map" in a;
3165
+ return a ? e ? ce(a) : de(a) : {
3166
+ value: [],
3167
+ length: 0,
3168
+ pending: !1,
3169
+ errors: [],
3170
+ valid: !0,
3171
+ invalid: !1,
3172
+ touched: !1,
3173
+ dirty: !1
3174
+ };
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
+ }
3233
+ export {
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
3271
+ };