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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/dist/behaviors/compute-from.d.ts +2 -0
  2. package/dist/behaviors/compute-from.js +31 -0
  3. package/dist/behaviors/copy-from.d.ts +2 -0
  4. package/dist/behaviors/copy-from.js +29 -0
  5. package/dist/behaviors/enable-when.d.ts +2 -0
  6. package/dist/behaviors/enable-when.js +25 -0
  7. package/dist/behaviors/reset-when.d.ts +2 -0
  8. package/dist/behaviors/reset-when.js +24 -0
  9. package/dist/behaviors/revalidate-when.d.ts +2 -0
  10. package/dist/behaviors/revalidate-when.js +18 -0
  11. package/dist/behaviors/sync-fields.d.ts +2 -0
  12. package/dist/behaviors/sync-fields.js +41 -0
  13. package/dist/behaviors/transform-value.d.ts +2 -0
  14. package/dist/behaviors/transform-value.js +45 -0
  15. package/dist/behaviors/watch-field.d.ts +2 -0
  16. package/dist/behaviors/watch-field.js +21 -0
  17. package/dist/behaviors.js +27 -20
  18. package/dist/core/behavior/behavior-context.d.ts +27 -13
  19. package/dist/core/behavior/behavior-registry.d.ts +15 -27
  20. package/dist/core/behavior/behaviors/compute-from.d.ts +50 -21
  21. package/dist/core/behavior/behaviors/copy-from.d.ts +39 -14
  22. package/dist/core/behavior/behaviors/enable-when.d.ts +88 -19
  23. package/dist/core/behavior/behaviors/reset-when.d.ts +31 -18
  24. package/dist/core/behavior/behaviors/revalidate-when.d.ts +40 -17
  25. package/dist/core/behavior/behaviors/sync-fields.d.ts +34 -14
  26. package/dist/core/behavior/behaviors/transform-value.d.ts +116 -44
  27. package/dist/core/behavior/behaviors/watch-field.d.ts +66 -21
  28. package/dist/core/behavior/compose-behavior.d.ts +2 -12
  29. package/dist/core/behavior/index.d.ts +0 -1
  30. package/dist/core/behavior/types.d.ts +2 -8
  31. package/dist/core/factories/node-factory.d.ts +6 -29
  32. package/dist/core/nodes/array-node.d.ts +42 -22
  33. package/dist/core/nodes/field-node.d.ts +51 -26
  34. package/dist/core/nodes/form-node.d.ts +18 -20
  35. package/dist/core/nodes/group-node.d.ts +37 -212
  36. package/dist/core/types/deep-schema.d.ts +2 -12
  37. package/dist/core/types/field-path.d.ts +1 -1
  38. package/dist/core/types/form-context.d.ts +27 -27
  39. package/dist/core/types/{group-node-proxy.d.ts → form-proxy.d.ts} +12 -42
  40. package/dist/core/types/index.d.ts +52 -6
  41. package/dist/core/types/validation-schema.d.ts +3 -12
  42. package/dist/core/utils/abstract-registry.d.ts +74 -0
  43. package/dist/core/utils/aggregate-signals.d.ts +71 -0
  44. package/dist/core/utils/create-form.d.ts +3 -20
  45. package/dist/core/utils/error-handler.d.ts +1 -18
  46. package/dist/core/utils/field-path-navigator.d.ts +1 -1
  47. package/dist/core/{validation → utils}/field-path.d.ts +23 -6
  48. package/dist/core/utils/form-observer.d.ts +176 -0
  49. package/dist/core/utils/form-proxy-builder.d.ts +25 -0
  50. package/dist/core/utils/form-submitter.d.ts +121 -0
  51. package/dist/core/utils/index.d.ts +10 -2
  52. package/dist/core/utils/registry-helpers.d.ts +0 -7
  53. package/dist/core/utils/safe-effect.d.ts +73 -0
  54. package/dist/core/utils/status-machine.d.ts +153 -0
  55. package/dist/core/utils/type-guards.d.ts +5 -23
  56. package/dist/core/utils/unique-id.d.ts +53 -0
  57. package/dist/core/validation/core/apply-when.d.ts +3 -9
  58. package/dist/core/validation/core/apply.d.ts +2 -13
  59. package/dist/core/validation/core/validate-async.d.ts +2 -8
  60. package/dist/core/validation/core/validate-tree.d.ts +0 -6
  61. package/dist/core/validation/core/validate.d.ts +1 -7
  62. package/dist/core/validation/index.d.ts +8 -2
  63. package/dist/core/validation/validate-form.d.ts +1 -38
  64. package/dist/core/validation/validation-applicator.d.ts +2 -21
  65. package/dist/core/validation/validation-context.d.ts +67 -28
  66. package/dist/core/validation/validation-registry.d.ts +11 -25
  67. package/dist/core/validation/validators/array-validators.d.ts +2 -12
  68. package/dist/core/validation/validators/date-utils.d.ts +26 -0
  69. package/dist/core/validation/validators/email.d.ts +2 -9
  70. package/dist/core/validation/validators/future-date.d.ts +35 -0
  71. package/dist/core/validation/validators/index.d.ts +7 -1
  72. package/dist/core/validation/validators/is-date.d.ts +36 -0
  73. package/dist/core/validation/validators/max-age.d.ts +36 -0
  74. package/dist/core/validation/validators/max-date.d.ts +36 -0
  75. package/dist/core/validation/validators/max-length.d.ts +3 -10
  76. package/dist/core/validation/validators/max.d.ts +3 -10
  77. package/dist/core/validation/validators/min-age.d.ts +36 -0
  78. package/dist/core/validation/validators/min-date.d.ts +36 -0
  79. package/dist/core/validation/validators/min-length.d.ts +3 -10
  80. package/dist/core/validation/validators/min.d.ts +3 -10
  81. package/dist/core/validation/validators/number.d.ts +2 -9
  82. package/dist/core/validation/validators/past-date.d.ts +35 -0
  83. package/dist/core/validation/validators/pattern.d.ts +2 -9
  84. package/dist/core/validation/validators/phone.d.ts +2 -9
  85. package/dist/core/validation/validators/required.d.ts +2 -9
  86. package/dist/core/validation/validators/url.d.ts +2 -9
  87. package/dist/date-utils-xUWFslTj.js +29 -0
  88. package/dist/field-path-DuKdGcIE.js +66 -0
  89. package/dist/hooks/types.d.ts +328 -0
  90. package/dist/hooks/useArrayLength.d.ts +31 -0
  91. package/dist/hooks/useFormControl.d.ts +15 -39
  92. package/dist/hooks/useFormControlValue.d.ts +167 -0
  93. package/dist/hooks/useHiddenCondition.d.ts +25 -0
  94. package/dist/hooks/useSignalSubscription.d.ts +17 -0
  95. package/dist/index-D25LsbRm.js +73 -0
  96. package/dist/index.d.ts +5 -1
  97. package/dist/index.js +1248 -1357
  98. package/dist/registry-helpers-Bv_BJ1s-.js +615 -0
  99. package/dist/safe-effect-Dh8uw81c.js +20 -0
  100. package/dist/validate-C3XiA_zf.js +10 -0
  101. package/dist/validators/email.d.ts +2 -0
  102. package/dist/validators/email.js +13 -0
  103. package/dist/validators/future-date.d.ts +2 -0
  104. package/dist/validators/future-date.js +20 -0
  105. package/dist/validators/is-date.d.ts +2 -0
  106. package/dist/validators/is-date.js +12 -0
  107. package/dist/validators/max-age.d.ts +2 -0
  108. package/dist/validators/max-age.js +20 -0
  109. package/dist/validators/max-date.d.ts +2 -0
  110. package/dist/validators/max-date.js +20 -0
  111. package/dist/validators/max-length.d.ts +2 -0
  112. package/dist/validators/max-length.js +11 -0
  113. package/dist/validators/max.d.ts +2 -0
  114. package/dist/validators/max.js +11 -0
  115. package/dist/validators/min-age.d.ts +2 -0
  116. package/dist/validators/min-age.js +20 -0
  117. package/dist/validators/min-date.d.ts +2 -0
  118. package/dist/validators/min-date.js +20 -0
  119. package/dist/validators/min-length.d.ts +2 -0
  120. package/dist/validators/min-length.js +11 -0
  121. package/dist/validators/min.d.ts +2 -0
  122. package/dist/validators/min.js +11 -0
  123. package/dist/validators/number.d.ts +2 -0
  124. package/dist/validators/number.js +35 -0
  125. package/dist/validators/past-date.d.ts +2 -0
  126. package/dist/validators/past-date.js +20 -0
  127. package/dist/validators/pattern.d.ts +2 -0
  128. package/dist/validators/pattern.js +11 -0
  129. package/dist/validators/phone.d.ts +2 -0
  130. package/dist/validators/phone.js +35 -0
  131. package/dist/validators/required.d.ts +2 -0
  132. package/dist/validators/required.js +15 -0
  133. package/dist/validators/url.d.ts +2 -0
  134. package/dist/validators/url.js +19 -0
  135. package/dist/validators-BGsNOgT1.js +207 -0
  136. package/dist/validators.js +53 -28
  137. package/llms.txt +8069 -357
  138. package/package.json +87 -8
  139. package/dist/behaviors-BRaiR-UY.js +0 -528
  140. package/dist/core/behavior/behavior-applicator.d.ts +0 -71
  141. package/dist/core/behavior/create-field-path.d.ts +0 -20
  142. package/dist/core/context/form-context-impl.d.ts +0 -29
  143. package/dist/core/nodes/group-node/field-registry.d.ts +0 -191
  144. package/dist/core/nodes/group-node/index.d.ts +0 -11
  145. package/dist/core/nodes/group-node/proxy-builder.d.ts +0 -71
  146. package/dist/core/nodes/group-node/state-manager.d.ts +0 -184
  147. package/dist/core/utils/debounce.d.ts +0 -160
  148. package/dist/core/utils/resources.d.ts +0 -41
  149. package/dist/core/validation/validators/date.d.ts +0 -38
  150. package/dist/registry-helpers-BfCZcMkO.js +0 -426
  151. package/dist/validators-DjXtDVoE.js +0 -455
@@ -1,21 +1,9 @@
1
- /**
2
- * GroupNode - узел группы полей формы
3
- *
4
- * Представляет группу полей (объект), где каждое поле может быть:
5
- * - FieldNode (простое поле)
6
- * - GroupNode (вложенная группа)
7
- * - ArrayNode (массив форм)
8
- *
9
- * Наследует от FormNode и реализует все его абстрактные методы
10
- *
11
- * @group Nodes
12
- */
13
- import type { ReadonlySignal } from '@preact/signals-core';
14
- import { FormNode, type SetValueOptions } from './form-node';
15
- import type { ValidationError, FieldStatus, ValidationSchemaFn, ValidatorRegistration, FormSchema, GroupNodeConfig, FormValue } from '../types';
16
- import type { GroupNodeWithControls } from '../types/group-node-proxy';
17
- import type { BehaviorSchemaFn } from '../behavior/types';
18
- import { FieldRegistry } from './group-node/field-registry';
1
+ import { ReadonlySignal } from '@preact/signals-core';
2
+ import { FormNode, SetValueOptions } from './form-node';
3
+ import { ValidationError, FieldStatus, ValidationSchemaFn, ValidatorRegistration, FormSchema, GroupNodeConfig, FormValue } from '../types';
4
+ import { FormProxy } from '../types/form-proxy';
5
+ import { BehaviorSchemaFn } from '../behavior/types';
6
+ import { SubmitOptions, SubmitResult } from '../utils/form-submitter';
19
7
  /**
20
8
  * GroupNode - узел для группы полей
21
9
  *
@@ -57,67 +45,47 @@ import { FieldRegistry } from './group-node/field-registry';
57
45
  * ```
58
46
  */
59
47
  export declare class GroupNode<T> extends FormNode<T> {
60
- id: string;
48
+ id: `${string}-${string}-${string}-${string}-${string}`;
61
49
  /**
62
- * Реестр полей формы
63
- * Использует FieldRegistry для инкапсуляции логики управления коллекцией полей
50
+ * Коллекция полей формы (упрощённый Map вместо FieldRegistry)
64
51
  */
65
- private fieldRegistry;
66
- /**
67
- * Строитель Proxy для типобезопасного доступа к полям
68
- * Использует ProxyBuilder для создания Proxy с расширенной функциональностью
69
- */
70
- private proxyBuilder;
71
- /**
72
- * Менеджер состояния формы
73
- * Инкапсулирует всю логику создания и управления сигналами состояния
74
- * Извлечен из GroupNode для соблюдения SRP
75
- */
76
- private stateManager;
52
+ private readonly _fields;
77
53
  /**
78
54
  * Менеджер подписок для централизованного cleanup
79
- * Использует SubscriptionManager вместо массива для управления подписками
80
55
  */
81
56
  private disposers;
82
57
  /**
83
58
  * Ссылка на Proxy-инстанс для использования в BehaviorContext
84
- * Устанавливается в конструкторе до применения behavior schema
85
59
  */
86
60
  private _proxyInstance?;
87
61
  /**
88
62
  * Навигатор для работы с путями к полям
89
- * Использует композицию вместо дублирования логики парсинга путей
90
63
  */
91
64
  private readonly pathNavigator;
92
65
  /**
93
66
  * Фабрика для создания узлов формы
94
- * Использует композицию для централизованного создания FieldNode/GroupNode/ArrayNode
95
67
  */
96
68
  private readonly nodeFactory;
97
69
  /**
98
70
  * Реестр валидаторов для этой формы
99
- * Использует композицию вместо глобального Singleton
100
- * Обеспечивает полную изоляцию форм друг от друга
71
+ * Может быть инжектирован через config._validationRegistry для тестирования
101
72
  */
102
73
  private readonly validationRegistry;
103
74
  /**
104
75
  * Реестр behaviors для этой формы
105
- * Использует композицию вместо глобального Singleton
106
- * Обеспечивает полную изоляцию форм друг от друга
76
+ * Может быть инжектирован через config._behaviorRegistry для тестирования
107
77
  */
108
78
  private readonly behaviorRegistry;
109
79
  /**
110
80
  * Аппликатор для применения валидаторов к форме
111
- * Извлечен из GroupNode для соблюдения SRP
112
- * Использует композицию для управления процессом валидации
113
81
  */
114
82
  private readonly validationApplicator;
115
- /**
116
- * Аппликатор для применения behavior схемы к форме
117
- * Извлечен из GroupNode для соблюдения SRP
118
- * Использует композицию для управления процессом применения behaviors
119
- */
120
- private readonly behaviorApplicator;
83
+ /** Управление отправкой формы */
84
+ private readonly formSubmitter;
85
+ /** Флаг disabled состояния */
86
+ private readonly _disabled;
87
+ /** Form-level validation errors */
88
+ private readonly _formErrors;
121
89
  readonly value: ReadonlySignal<T>;
122
90
  readonly valid: ReadonlySignal<boolean>;
123
91
  readonly invalid: ReadonlySignal<boolean>;
@@ -135,6 +103,11 @@ export declare class GroupNode<T> extends FormNode<T> {
135
103
  * Создать GroupNode с полной конфигурацией (form, behavior, validation)
136
104
  */
137
105
  constructor(config: GroupNodeConfig<T>);
106
+ /**
107
+ * Создать Proxy для типобезопасного доступа к полям
108
+ * @see buildFormProxy
109
+ */
110
+ private buildProxy;
138
111
  getValue(): T;
139
112
  setValue(value: T, options?: SetValueOptions): void;
140
113
  patchValue(value: Partial<T>): void;
@@ -158,50 +131,11 @@ export declare class GroupNode<T> extends FormNode<T> {
158
131
  reset(value?: T): void;
159
132
  /**
160
133
  * Сбросить форму к исходным значениям (initialValues)
161
- *
162
- * @remarks
163
- * Рекурсивно вызывает resetToInitial() для всех полей формы.
164
- * Более явный способ сброса к начальным значениям по сравнению с reset()
165
- *
166
- * Полезно когда:
167
- * - Пользователь нажал "Cancel" - полная отмена изменений
168
- * - Форма была изменена через reset(newValues), но нужно вернуться к самому началу
169
- * - Явное намерение показать "отмена всех изменений"
170
- *
171
- * @example
172
- * ```typescript
173
- * const form = new GroupNode({
174
- * email: { value: 'initial@mail.com', component: Input },
175
- * name: { value: 'John', component: Input }
176
- * });
177
- *
178
- * form.email.setValue('changed@mail.com');
179
- * form.reset({ email: 'temp@mail.com', name: 'Jane' });
180
- * console.log(form.getValue()); // { email: 'temp@mail.com', name: 'Jane' }
181
- *
182
- * form.resetToInitial();
183
- * console.log(form.getValue()); // { email: 'initial@mail.com', name: 'John' }
184
- * ```
185
134
  */
186
135
  resetToInitial(): void;
187
136
  validate(): Promise<boolean>;
188
137
  /**
189
138
  * Установить form-level validation errors
190
- * Используется для server-side validation или кросс-полевых ошибок
191
- *
192
- * @param errors - массив ошибок уровня формы
193
- *
194
- * @example
195
- * ```typescript
196
- * // Server-side validation после submit
197
- * try {
198
- * await api.createUser(form.getValue());
199
- * } catch (error) {
200
- * form.setErrors([
201
- * { code: 'duplicate_email', message: 'Email уже используется' }
202
- * ]);
203
- * }
204
- * ```
205
139
  */
206
140
  setErrors(errors: ValidationError[]): void;
207
141
  /**
@@ -210,29 +144,12 @@ export declare class GroupNode<T> extends FormNode<T> {
210
144
  clearErrors(): void;
211
145
  /**
212
146
  * Получить поле по ключу
213
- *
214
- * Публичный метод для доступа к полю из fieldRegistry
215
- *
216
- * @param key - Ключ поля
217
- * @returns FormNode или undefined, если поле не найдено
218
- *
219
- * @example
220
- * ```typescript
221
- * const emailField = form.getField('email');
222
- * if (emailField) {
223
- * console.log(emailField.value.value);
224
- * }
225
- * ```
226
147
  */
227
148
  getField<K extends keyof T>(key: K): FormNode<T[K]> | undefined;
228
149
  /**
229
- * Получить Map всех полей формы
230
- *
231
- * Используется в FieldPathNavigator для навигации по полям
232
- *
233
- * @returns Map полей формы
150
+ * Получить Map всех полей формы (для совместимости)
234
151
  */
235
- get fields(): FieldRegistry<T>;
152
+ get fields(): Map<keyof T, FormNode<FormValue>>;
236
153
  /**
237
154
  * Получить Proxy-инстанс для прямого доступа к полям
238
155
  *
@@ -259,52 +176,31 @@ export declare class GroupNode<T> extends FormNode<T> {
259
176
  * console.log(proxy.email.value); // Прямой доступ к полю
260
177
  * ```
261
178
  */
262
- getProxy(): GroupNodeWithControls<T>;
179
+ getProxy(): FormProxy<T>;
263
180
  /**
264
181
  * Получить все поля формы как итератор
265
- *
266
- * Предоставляет доступ к внутренним полям для валидации и других операций
267
- *
268
- * @returns Итератор по всем полям формы
269
- *
270
- * @example
271
- * ```typescript
272
- * // Валидация всех полей
273
- * await Promise.all(
274
- * Array.from(form.getAllFields()).map(field => field.validate())
275
- * );
276
- * ```
277
182
  */
278
183
  getAllFields(): IterableIterator<FormNode<FormValue>>;
279
- /**
280
- * Hook: вызывается после markAsTouched()
281
- *
282
- * Для GroupNode: рекурсивно помечаем все дочерние поля как touched
283
- */
284
184
  protected onMarkAsTouched(): void;
285
- /**
286
- * Hook: вызывается после markAsUntouched()
287
- *
288
- * Для GroupNode: рекурсивно помечаем все дочерние поля как untouched
289
- */
290
185
  protected onMarkAsUntouched(): void;
291
- /**
292
- * Hook: вызывается после markAsDirty()
293
- *
294
- * Для GroupNode: рекурсивно помечаем все дочерние поля как dirty
295
- */
296
186
  protected onMarkAsDirty(): void;
187
+ protected onMarkAsPristine(): void;
297
188
  /**
298
- * Hook: вызывается после markAsPristine()
189
+ * Отправить форму
299
190
  *
300
- * Для GroupNode: рекурсивно помечаем все дочерние поля как pristine
191
+ * @param onSubmit - Callback для отправки данных
192
+ * @param options - Опции submit (skipValidation, skipTouch)
193
+ * @returns Результат от onSubmit или null если валидация не пройдена
301
194
  */
302
- protected onMarkAsPristine(): void;
195
+ submit<R>(onSubmit: (values: T) => Promise<R> | R, options?: SubmitOptions): Promise<R | null>;
303
196
  /**
304
- * Отправить форму
305
- * Валидирует форму и вызывает onSubmit если форма валидна
197
+ * Отправить форму с расширенным результатом
198
+ *
199
+ * @param onSubmit - Callback для отправки данных
200
+ * @param options - Опции submit
201
+ * @returns Объект SubmitResult с данными, статусом и возможной ошибкой
306
202
  */
307
- submit<R>(onSubmit: (values: T) => Promise<R> | R): Promise<R | null>;
203
+ submitWithResult<R>(onSubmit: (values: T) => Promise<R> | R, options?: SubmitOptions): Promise<SubmitResult<R>>;
308
204
  /**
309
205
  * Применить validation schema к форме
310
206
  *
@@ -314,41 +210,7 @@ export declare class GroupNode<T> extends FormNode<T> {
314
210
  applyValidationSchema(schemaFn: ValidationSchemaFn<T>): void;
315
211
  /**
316
212
  * Применить behavior schema к форме
317
- *
318
- * ✅ РЕФАКТОРИНГ: Делегирование BehaviorApplicator (SRP)
319
- *
320
- * Логика применения behavior схемы извлечена в BehaviorApplicator для:
321
- * - Соблюдения Single Responsibility Principle
322
- * - Уменьшения размера GroupNode (~50 строк)
323
- * - Улучшения тестируемости
324
- * - Консистентности с ValidationApplicator
325
- *
326
- * @param schemaFn Функция описания поведения формы
327
213
  * @returns Функция cleanup для отписки от всех behaviors
328
- *
329
- * @example
330
- * ```typescript
331
- * import { copyFrom, enableWhen, computeFrom } from '@/lib/forms/core/behaviors';
332
- *
333
- * const behaviorSchema: BehaviorSchemaFn<MyForm> = (path) => {
334
- * copyFrom(path.residenceAddress, path.registrationAddress, {
335
- * when: (form) => form.sameAsRegistration === true
336
- * });
337
- *
338
- * enableWhen(path.propertyValue, (form) => form.loanType === 'mortgage');
339
- *
340
- * computeFrom(
341
- * path.initialPayment,
342
- * [path.propertyValue],
343
- * (propertyValue) => propertyValue ? propertyValue * 0.2 : null
344
- * );
345
- * };
346
- *
347
- * const cleanup = form.applyBehaviorSchema(behaviorSchema);
348
- *
349
- * // Cleanup при unmount
350
- * useEffect(() => cleanup, []);
351
- * ```
352
214
  */
353
215
  applyBehaviorSchema(schemaFn: BehaviorSchemaFn<T>): () => void;
354
216
  /**
@@ -412,29 +274,6 @@ export declare class GroupNode<T> extends FormNode<T> {
412
274
  private createNode;
413
275
  /**
414
276
  * Связывает два поля: при изменении source автоматически обновляется target
415
- * Поддерживает опциональную трансформацию значения
416
- *
417
- * @param sourceKey - Ключ поля-источника
418
- * @param targetKey - Ключ поля-цели
419
- * @param transform - Опциональная функция трансформации значения
420
- * @returns Функция отписки для cleanup
421
- *
422
- * @example
423
- * ```typescript
424
- * // Автоматический расчет минимального взноса от стоимости недвижимости
425
- * const dispose = form.linkFields(
426
- * 'propertyValue',
427
- * 'initialPayment',
428
- * (propertyValue) => propertyValue ? propertyValue * 0.2 : null
429
- * );
430
- *
431
- * // При изменении propertyValue → автоматически обновится initialPayment
432
- * form.propertyValue.setValue(1000000);
433
- * // initialPayment станет 200000
434
- *
435
- * // Cleanup
436
- * useEffect(() => dispose, []);
437
- * ```
438
277
  */
439
278
  linkFields<K1 extends keyof T, K2 extends keyof T>(sourceKey: K1, targetKey: K2, transform?: (value: T[K1]) => T[K2]): () => void;
440
279
  /**
@@ -467,28 +306,14 @@ export declare class GroupNode<T> extends FormNode<T> {
467
306
  watchField<K extends keyof T>(fieldPath: K extends string ? K : string, callback: (value: T[K]) => void | Promise<void>): () => void;
468
307
  /**
469
308
  * Hook: вызывается после disable()
470
- *
471
- * Для GroupNode: рекурсивно отключаем все дочерние поля
472
309
  */
473
310
  protected onDisable(): void;
474
311
  /**
475
312
  * Hook: вызывается после enable()
476
- *
477
- * Для GroupNode: рекурсивно включаем все дочерние поля
478
313
  */
479
314
  protected onEnable(): void;
480
315
  /**
481
316
  * Очистить все ресурсы узла
482
- * Рекурсивно очищает все subscriptions и дочерние узлы
483
- *
484
- * @example
485
- * ```typescript
486
- * useEffect(() => {
487
- * return () => {
488
- * form.dispose();
489
- * };
490
- * }, []);
491
- * ```
492
317
  */
493
318
  dispose(): void;
494
319
  }
@@ -1,15 +1,5 @@
1
- /**
2
- * Типы для схемы формы (Deep Schema)
3
- *
4
- * Автоматическое определение типов схемы на основе структуры данных:
5
- * - `[{...}]` → массив форм
6
- * - `{...}` → вложенная группа
7
- * - `{value, component}` → конфигурация поля
8
- *
9
- * @group Types
10
- */
11
- import type { ComponentType } from 'react';
12
- import type { ValidatorFn, AsyncValidatorFn, FormFields, AnyFunction } from './index';
1
+ import { ComponentType } from 'react';
2
+ import { ValidatorFn, AsyncValidatorFn, FormFields, AnyFunction } from './index';
13
3
  /**
14
4
  * Конфигурация поля
15
5
  * @group Types
@@ -1,4 +1,4 @@
1
- import type { AnyFunction } from './index';
1
+ import { AnyFunction } from './index';
2
2
  /**
3
3
  * FieldPath предоставляет типобезопасный доступ к путям полей формы
4
4
  *
@@ -1,29 +1,7 @@
1
- /**
2
- * Единый контекст для работы с формой
3
- *
4
- * Используется в:
5
- * - Behavior схемах (watchField, copyFrom, transformValue, etc.)
6
- * - Validation схемах (validate, validateAsync, validateTree)
7
- *
8
- * @example
9
- * ```typescript
10
- * // Behavior
11
- * watchField(path.country, (country, ctx) => {
12
- * ctx.form.city.updateComponentProps({ options: cities });
13
- * ctx.setFieldValue('city', null);
14
- * });
15
- *
16
- * // Validation
17
- * validate(path.email, (value, ctx) => {
18
- * if (!value) return { code: 'required', message: 'Required' };
19
- * const confirm = ctx.form.confirmEmail.value.value;
20
- * if (value !== confirm) return { code: 'mismatch', message: 'Emails must match' };
21
- * return null;
22
- * });
23
- * ```
24
- */
25
- import type { GroupNodeWithControls } from './group-node-proxy';
26
- import type { FieldPathNode } from './field-path';
1
+ import { FormProxy } from './form-proxy';
2
+ import { FieldPathNode } from './field-path';
3
+ import { FormNode } from '../nodes/form-node';
4
+ import { FormValue } from './index';
27
5
  /**
28
6
  * Единый контекст для работы с формой
29
7
  *
@@ -62,7 +40,7 @@ export interface FormContext<TForm> {
62
40
  * ctx.form.items.clear()
63
41
  * ```
64
42
  */
65
- readonly form: GroupNodeWithControls<TForm>;
43
+ readonly form: FormProxy<TForm>;
66
44
  /**
67
45
  * Безопасно установить значение поля по строковому пути или FieldPath
68
46
  *
@@ -86,4 +64,26 @@ export interface FormContext<TForm> {
86
64
  * ```
87
65
  */
88
66
  setFieldValue(path: string | FieldPathNode<TForm, unknown>, value: unknown): void;
67
+ /**
68
+ * Получить поле формы по строковому пути
69
+ *
70
+ * Используется для динамического доступа к вложенным полям,
71
+ * особенно в модульных behavior схемах, применяемых через apply().
72
+ *
73
+ * @param path - Строковый путь к полю (например "address.city")
74
+ * @returns FormNode или undefined если поле не найдено
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * // В модульной behavior схеме, применяемой к вложенному полю:
79
+ * // apply([path.registrationAddress, path.residenceAddress], addressBehavior)
80
+ *
81
+ * watchField(path.region, (region, ctx) => {
82
+ * // path.city.__path = "registrationAddress.city" или "residenceAddress.city"
83
+ * const cityField = ctx.getFieldByPath(path.city.__path);
84
+ * cityField?.updateComponentProps({ options: cities });
85
+ * });
86
+ * ```
87
+ */
88
+ getFieldByPath(path: string): FormNode<FormValue> | undefined;
89
89
  }
@@ -1,34 +1,4 @@
1
- /**
2
- * Типы для Proxy-доступа к полям GroupNode
3
- *
4
- * Решает проблему типизации, когда GroupNode<T> использует Proxy для прямого доступа к полям.
5
- * TypeScript не может автоматически определить правильные типы для вложенных форм и массивов.
6
- *
7
- * @group Types
8
- *
9
- * @example
10
- * ```typescript
11
- * interface MyForm {
12
- * name: string;
13
- * address: {
14
- * city: string;
15
- * };
16
- * items: Array<{ title: string }>;
17
- * }
18
- *
19
- * const form: GroupNodeWithControls<MyForm> = new GroupNode(schema);
20
- *
21
- * // TypeScript знает, что это FieldNode<string>
22
- * form.name.setValue('John');
23
- *
24
- * // TypeScript знает, что это GroupNode<{city: string}>
25
- * form.address.city.setValue('Moscow');
26
- *
27
- * // TypeScript знает, что это ArrayNode<{title: string}>
28
- * form.items.push({ title: 'New Item' });
29
- * ```
30
- */
31
- import type { FormFields } from './index';
1
+ import { FormFields } from './index';
32
2
  type FieldNode<_T> = any;
33
3
  type GroupNode<_T> = any;
34
4
  type ArrayNode<_T> = any;
@@ -36,9 +6,9 @@ type ArrayNode<_T> = any;
36
6
  * Мапит тип модели данных T на правильные типы узлов формы
37
7
  *
38
8
  * Рекурсивно определяет типы узлов на основе структуры данных:
39
- * - `T[K] extends Array<infer U>` где U - объект → `ArrayNodeWithControls<U>`
9
+ * - `T[K] extends Array<infer U>` где U - объект → `FormArrayProxy<U>`
40
10
  * - `T[K] extends Array<infer U>` где U - примитив → `FieldNode<T[K]>` (массив как обычное поле)
41
- * - `T[K] extends object` → `GroupNodeWithControls<T[K]>` (вложенная форма с типизацией)
11
+ * - `T[K] extends object` → `FormProxy<T[K]>` (вложенная форма с типизацией)
42
12
  * - `T[K]` примитив → `FieldNode<T[K]>` (простое поле)
43
13
  *
44
14
  * Использует NonNullable для правильной обработки опциональных полей
@@ -48,8 +18,8 @@ type ArrayNode<_T> = any;
48
18
  *
49
19
  * @template T - Тип модели данных формы
50
20
  */
51
- export type FormNodeControls<T> = {
52
- [K in keyof T]: NonNullable<T[K]> extends Array<infer U> ? U extends FormFields ? ArrayNodeWithControls<U> : FieldNode<T[K]> : NonNullable<T[K]> extends FormFields ? NonNullable<T[K]> extends Date | File | Blob ? FieldNode<T[K]> : GroupNodeWithControls<NonNullable<T[K]>> : FieldNode<T[K]>;
21
+ export type FormControlsProxy<T> = {
22
+ [K in keyof T]: NonNullable<T[K]> extends Array<infer U> ? U extends FormFields ? FormArrayProxy<U> : FieldNode<T[K]> : NonNullable<T[K]> extends FormFields ? NonNullable<T[K]> extends Date | File | Blob ? FieldNode<T[K]> : FormProxy<NonNullable<T[K]>> : FieldNode<T[K]>;
53
23
  };
54
24
  /**
55
25
  * Комбинированный тип для GroupNode с Proxy доступом к полям
@@ -72,7 +42,7 @@ export type FormNodeControls<T> = {
72
42
  * };
73
43
  * }
74
44
  *
75
- * const form: GroupNodeWithControls<UserForm> = new GroupNode(schema);
45
+ * const form = createForm<UserForm>(schema);
76
46
  *
77
47
  * // Доступ к методам GroupNode
78
48
  * await form.validate();
@@ -84,7 +54,7 @@ export type FormNodeControls<T> = {
84
54
  * form.profile.name.setValue('John');
85
55
  * ```
86
56
  */
87
- export type GroupNodeWithControls<T> = GroupNode<T> & FormNodeControls<T>;
57
+ export type FormProxy<T> = GroupNode<T> & FormControlsProxy<T>;
88
58
  /**
89
59
  * Комбинированный тип для ArrayNode с Proxy доступом к элементам
90
60
  *
@@ -102,7 +72,7 @@ export type GroupNodeWithControls<T> = GroupNode<T> & FormNodeControls<T>;
102
72
  * completed: boolean;
103
73
  * }
104
74
  *
105
- * const todos: ArrayNodeWithControls<TodoItem> = new ArrayNode(schema);
75
+ * const todos: FormArrayProxy<TodoItem> = new ArrayNode(schema);
106
76
  *
107
77
  * // Доступ к методам ArrayNode
108
78
  * todos.push({ title: 'New todo', completed: false });
@@ -117,19 +87,19 @@ export type GroupNodeWithControls<T> = GroupNode<T> & FormNodeControls<T>;
117
87
  * });
118
88
  * ```
119
89
  */
120
- export type ArrayNodeWithControls<T extends FormFields> = ArrayNode<T> & {
90
+ export type FormArrayProxy<T extends FormFields> = ArrayNode<T> & {
121
91
  /**
122
92
  * Безопасный доступ к элементу массива по индексу
123
93
  * Возвращает GroupNode с типизированными полями или undefined
124
94
  */
125
- at(index: number): GroupNodeWithControls<T> | undefined;
95
+ at(index: number): FormProxy<T> | undefined;
126
96
  /**
127
97
  * Итерация по элементам массива с типизированными элементами
128
98
  */
129
- forEach(callback: (item: GroupNodeWithControls<T>, index: number) => void): void;
99
+ forEach(callback: (item: FormProxy<T>, index: number) => void): void;
130
100
  /**
131
101
  * Маппинг элементов массива с типизированными элементами
132
102
  */
133
- map<R>(callback: (item: GroupNodeWithControls<T>, index: number) => R): R[];
103
+ map<R>(callback: (item: FormProxy<T>, index: number) => R): R[];
134
104
  };
135
105
  export {};
@@ -1,3 +1,7 @@
1
+ import { BehaviorSchemaFn } from '../behavior/types';
2
+ import { FormSchema } from './deep-schema';
3
+ import { ValidationSchemaFn } from './validation-schema';
4
+ import { FormNode } from '../nodes/form-node';
1
5
  /**
2
6
  * Represents any valid form value type
3
7
  * Use this instead of 'any' for form values to maintain type safety
@@ -20,12 +24,46 @@ export type UnknownFormValue = unknown;
20
24
  * @category Validation Types
21
25
  */
22
26
  export type ValidatorFn<T = FormValue> = (value: T) => ValidationError | null;
27
+ /**
28
+ * Опции для асинхронного валидатора
29
+ * @group Types
30
+ * @category Validation Types
31
+ */
32
+ export interface AsyncValidatorOptions {
33
+ /**
34
+ * AbortSignal для отмены валидации
35
+ * Позволяет отменить асинхронную операцию при новой валидации
36
+ */
37
+ signal?: AbortSignal;
38
+ }
23
39
  /**
24
40
  * Асинхронная функция валидации
41
+ *
42
+ * @param value - Значение для валидации
43
+ * @param options - Опции валидации (опционально)
44
+ * @returns Promise с ошибкой валидации или null если значение валидно
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * // Простой валидатор (без поддержки отмены)
49
+ * const emailExists: AsyncValidatorFn<string> = async (value) => {
50
+ * const exists = await checkEmail(value);
51
+ * return exists ? { code: 'exists', message: 'Email already exists' } : null;
52
+ * };
53
+ *
54
+ * // Валидатор с поддержкой отмены
55
+ * const emailExistsAbortable: AsyncValidatorFn<string> = async (value, options) => {
56
+ * const exists = await fetch(`/api/check-email?email=${value}`, {
57
+ * signal: options?.signal // Передаём signal в fetch для отмены запроса
58
+ * });
59
+ * return exists ? { code: 'exists', message: 'Email already exists' } : null;
60
+ * };
61
+ * ```
62
+ *
25
63
  * @group Types
26
64
  * @category Validation Types
27
65
  */
28
- export type AsyncValidatorFn<T = FormValue> = (value: T) => Promise<ValidationError | null>;
66
+ export type AsyncValidatorFn<T = FormValue> = (value: T, options?: AsyncValidatorOptions) => Promise<ValidationError | null>;
29
67
  /**
30
68
  * Ошибка валидации
31
69
  * @group Types
@@ -64,10 +102,7 @@ export type { FieldPath, FieldPathNode } from '../types/field-path';
64
102
  export type { ContextualValidatorFn, ContextualAsyncValidatorFn, TreeValidatorFn, ConditionFn, ValidateOptions, ValidateAsyncOptions, ValidateTreeOptions, ValidationSchemaFn, ValidatorRegistration, } from './validation-schema';
65
103
  export type { FormSchema, ArrayConfig } from './deep-schema';
66
104
  export type { FormContext } from './form-context';
67
- export type { FormNodeControls, GroupNodeWithControls, ArrayNodeWithControls, } from './group-node-proxy';
68
- import type { BehaviorSchemaFn } from '../behavior/types';
69
- import type { FormSchema } from './deep-schema';
70
- import type { ValidationSchemaFn } from './validation-schema';
105
+ export type { FormControlsProxy, FormProxy, FormArrayProxy } from './form-proxy';
71
106
  /**
72
107
  * Конфигурация GroupNode с поддержкой схем
73
108
  * Используется для создания форм с автоматическим применением behavior и validation схем
@@ -82,6 +117,18 @@ export interface GroupNodeConfig<T> {
82
117
  behavior?: BehaviorSchemaFn<T>;
83
118
  /** Схема валидации (required, email, minLength и т.д.) */
84
119
  validation?: ValidationSchemaFn<T>;
120
+ /**
121
+ * Опциональный ValidationRegistry для dependency injection
122
+ * Используется для тестирования с mock-реестрами
123
+ * @internal
124
+ */
125
+ _validationRegistry?: unknown;
126
+ /**
127
+ * Опциональный BehaviorRegistry для dependency injection
128
+ * Используется для тестирования с mock-реестрами
129
+ * @internal
130
+ */
131
+ _behaviorRegistry?: unknown;
85
132
  }
86
133
  /**
87
134
  * Тип для Record с unknown значениями
@@ -112,7 +159,6 @@ export interface ArrayNodeLike {
112
159
  at(index: number): FormNode<unknown> | undefined;
113
160
  length: unknown;
114
161
  }
115
- import type { FormNode } from '../nodes/form-node';
116
162
  /**
117
163
  * Конфиг с полем schema (для ArrayConfig)
118
164
  * @internal