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

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.
@@ -15,7 +15,6 @@ import { FormNode, type SetValueOptions } from './form-node';
15
15
  import type { ValidationError, FieldStatus, ValidationSchemaFn, ValidatorRegistration, FormSchema, GroupNodeConfig, FormValue } from '../types';
16
16
  import type { GroupNodeWithControls } from '../types/group-node-proxy';
17
17
  import type { BehaviorSchemaFn } from '../behavior/types';
18
- import { FieldRegistry } from './group-node/field-registry';
19
18
  /**
20
19
  * GroupNode - узел для группы полей
21
20
  *
@@ -59,65 +58,43 @@ import { FieldRegistry } from './group-node/field-registry';
59
58
  export declare class GroupNode<T> extends FormNode<T> {
60
59
  id: string;
61
60
  /**
62
- * Реестр полей формы
63
- * Использует FieldRegistry для инкапсуляции логики управления коллекцией полей
61
+ * Коллекция полей формы (упрощённый Map вместо FieldRegistry)
64
62
  */
65
- private fieldRegistry;
66
- /**
67
- * Строитель Proxy для типобезопасного доступа к полям
68
- * Использует ProxyBuilder для создания Proxy с расширенной функциональностью
69
- */
70
- private proxyBuilder;
71
- /**
72
- * Менеджер состояния формы
73
- * Инкапсулирует всю логику создания и управления сигналами состояния
74
- * Извлечен из GroupNode для соблюдения SRP
75
- */
76
- private stateManager;
63
+ private readonly _fields;
77
64
  /**
78
65
  * Менеджер подписок для централизованного cleanup
79
- * Использует SubscriptionManager вместо массива для управления подписками
80
66
  */
81
67
  private disposers;
82
68
  /**
83
69
  * Ссылка на Proxy-инстанс для использования в BehaviorContext
84
- * Устанавливается в конструкторе до применения behavior schema
85
70
  */
86
71
  private _proxyInstance?;
87
72
  /**
88
73
  * Навигатор для работы с путями к полям
89
- * Использует композицию вместо дублирования логики парсинга путей
90
74
  */
91
75
  private readonly pathNavigator;
92
76
  /**
93
77
  * Фабрика для создания узлов формы
94
- * Использует композицию для централизованного создания FieldNode/GroupNode/ArrayNode
95
78
  */
96
79
  private readonly nodeFactory;
97
80
  /**
98
81
  * Реестр валидаторов для этой формы
99
- * Использует композицию вместо глобального Singleton
100
- * Обеспечивает полную изоляцию форм друг от друга
101
82
  */
102
83
  private readonly validationRegistry;
103
84
  /**
104
85
  * Реестр behaviors для этой формы
105
- * Использует композицию вместо глобального Singleton
106
- * Обеспечивает полную изоляцию форм друг от друга
107
86
  */
108
87
  private readonly behaviorRegistry;
109
88
  /**
110
89
  * Аппликатор для применения валидаторов к форме
111
- * Извлечен из GroupNode для соблюдения SRP
112
- * Использует композицию для управления процессом валидации
113
90
  */
114
91
  private readonly validationApplicator;
115
- /**
116
- * Аппликатор для применения behavior схемы к форме
117
- * Извлечен из GroupNode для соблюдения SRP
118
- * Использует композицию для управления процессом применения behaviors
119
- */
120
- private readonly behaviorApplicator;
92
+ /** Флаг отправки формы */
93
+ private readonly _submitting;
94
+ /** Флаг disabled состояния */
95
+ private readonly _disabled;
96
+ /** Form-level validation errors */
97
+ private readonly _formErrors;
121
98
  readonly value: ReadonlySignal<T>;
122
99
  readonly valid: ReadonlySignal<boolean>;
123
100
  readonly invalid: ReadonlySignal<boolean>;
@@ -135,6 +112,10 @@ export declare class GroupNode<T> extends FormNode<T> {
135
112
  * Создать GroupNode с полной конфигурацией (form, behavior, validation)
136
113
  */
137
114
  constructor(config: GroupNodeConfig<T>);
115
+ /**
116
+ * Создать Proxy для типобезопасного доступа к полям
117
+ */
118
+ private buildProxy;
138
119
  getValue(): T;
139
120
  setValue(value: T, options?: SetValueOptions): void;
140
121
  patchValue(value: Partial<T>): void;
@@ -158,50 +139,11 @@ export declare class GroupNode<T> extends FormNode<T> {
158
139
  reset(value?: T): void;
159
140
  /**
160
141
  * Сбросить форму к исходным значениям (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
142
  */
186
143
  resetToInitial(): void;
187
144
  validate(): Promise<boolean>;
188
145
  /**
189
146
  * Установить 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
147
  */
206
148
  setErrors(errors: ValidationError[]): void;
207
149
  /**
@@ -210,29 +152,12 @@ export declare class GroupNode<T> extends FormNode<T> {
210
152
  clearErrors(): void;
211
153
  /**
212
154
  * Получить поле по ключу
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
155
  */
227
156
  getField<K extends keyof T>(key: K): FormNode<T[K]> | undefined;
228
157
  /**
229
- * Получить Map всех полей формы
230
- *
231
- * Используется в FieldPathNavigator для навигации по полям
232
- *
233
- * @returns Map полей формы
158
+ * Получить Map всех полей формы (для совместимости)
234
159
  */
235
- get fields(): FieldRegistry<T>;
160
+ get fields(): Map<keyof T, FormNode<FormValue>>;
236
161
  /**
237
162
  * Получить Proxy-инстанс для прямого доступа к полям
238
163
  *
@@ -262,47 +187,14 @@ export declare class GroupNode<T> extends FormNode<T> {
262
187
  getProxy(): GroupNodeWithControls<T>;
263
188
  /**
264
189
  * Получить все поля формы как итератор
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
190
  */
278
191
  getAllFields(): IterableIterator<FormNode<FormValue>>;
279
- /**
280
- * Hook: вызывается после markAsTouched()
281
- *
282
- * Для GroupNode: рекурсивно помечаем все дочерние поля как touched
283
- */
284
192
  protected onMarkAsTouched(): void;
285
- /**
286
- * Hook: вызывается после markAsUntouched()
287
- *
288
- * Для GroupNode: рекурсивно помечаем все дочерние поля как untouched
289
- */
290
193
  protected onMarkAsUntouched(): void;
291
- /**
292
- * Hook: вызывается после markAsDirty()
293
- *
294
- * Для GroupNode: рекурсивно помечаем все дочерние поля как dirty
295
- */
296
194
  protected onMarkAsDirty(): void;
297
- /**
298
- * Hook: вызывается после markAsPristine()
299
- *
300
- * Для GroupNode: рекурсивно помечаем все дочерние поля как pristine
301
- */
302
195
  protected onMarkAsPristine(): void;
303
196
  /**
304
197
  * Отправить форму
305
- * Валидирует форму и вызывает onSubmit если форма валидна
306
198
  */
307
199
  submit<R>(onSubmit: (values: T) => Promise<R> | R): Promise<R | null>;
308
200
  /**
@@ -314,41 +206,7 @@ export declare class GroupNode<T> extends FormNode<T> {
314
206
  applyValidationSchema(schemaFn: ValidationSchemaFn<T>): void;
315
207
  /**
316
208
  * Применить 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
209
  * @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
210
  */
353
211
  applyBehaviorSchema(schemaFn: BehaviorSchemaFn<T>): () => void;
354
212
  /**
@@ -412,29 +270,6 @@ export declare class GroupNode<T> extends FormNode<T> {
412
270
  private createNode;
413
271
  /**
414
272
  * Связывает два поля: при изменении 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
273
  */
439
274
  linkFields<K1 extends keyof T, K2 extends keyof T>(sourceKey: K1, targetKey: K2, transform?: (value: T[K1]) => T[K2]): () => void;
440
275
  /**
@@ -467,28 +302,14 @@ export declare class GroupNode<T> extends FormNode<T> {
467
302
  watchField<K extends keyof T>(fieldPath: K extends string ? K : string, callback: (value: T[K]) => void | Promise<void>): () => void;
468
303
  /**
469
304
  * Hook: вызывается после disable()
470
- *
471
- * Для GroupNode: рекурсивно отключаем все дочерние поля
472
305
  */
473
306
  protected onDisable(): void;
474
307
  /**
475
308
  * Hook: вызывается после enable()
476
- *
477
- * Для GroupNode: рекурсивно включаем все дочерние поля
478
309
  */
479
310
  protected onEnable(): void;
480
311
  /**
481
312
  * Очистить все ресурсы узла
482
- * Рекурсивно очищает все subscriptions и дочерние узлы
483
- *
484
- * @example
485
- * ```typescript
486
- * useEffect(() => {
487
- * return () => {
488
- * form.dispose();
489
- * };
490
- * }, []);
491
- * ```
492
313
  */
493
314
  dispose(): void;
494
315
  }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * FieldPath proxy - типобезопасный доступ к путям полей формы
3
+ *
4
+ * Единый модуль для validation и behavior схем.
5
+ * Предоставляет типизированную навигацию по структуре формы.
6
+ *
7
+ * @module utils/field-path
8
+ */
9
+ import type { FieldPath, FieldPathNode } from '../types';
10
+ /**
11
+ * Создать FieldPath proxy для формы
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * const path = createFieldPath<MyForm>();
16
+ * console.log(path.email.__path); // 'email'
17
+ * console.log(path.personalData.firstName.__path); // 'personalData.firstName'
18
+ * ```
19
+ */
20
+ export declare function createFieldPath<T>(): FieldPath<T>;
21
+ /**
22
+ * Извлечь путь из FieldPathNode
23
+ */
24
+ export declare function extractPath(node: FieldPathNode<unknown, unknown> | unknown): string;
25
+ /**
26
+ * Преобразовать FieldPathNode в FieldPath для переиспользования схем
27
+ *
28
+ * Позволяет композировать validation schemas:
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * const personalDataValidation = (path: FieldPath<PersonalData>) => {
33
+ * required(path.firstName, { message: 'Имя обязательно' });
34
+ * required(path.lastName, { message: 'Фамилия обязательна' });
35
+ * };
36
+ *
37
+ * const mainValidation = (path: FieldPath<MyForm>) => {
38
+ * // Переиспользуем схему
39
+ * personalDataValidation(toFieldPath(path.personalData));
40
+ * required(path.email);
41
+ * };
42
+ * ```
43
+ */
44
+ export declare function toFieldPath<T>(node: FieldPathNode<unknown, T, never> | FieldPathNode<any, T, any>): FieldPath<T>;
45
+ /**
46
+ * Извлечь ключ поля из FieldPathNode
47
+ */
48
+ export declare function extractKey(node: FieldPathNode<unknown, unknown> | unknown): string;
@@ -4,6 +4,7 @@
4
4
  * Централизованные вспомогательные классы и функции.
5
5
  */
6
6
  export { FieldPathNavigator, type PathSegment } from './field-path-navigator';
7
+ export { createFieldPath, extractPath, extractKey, toFieldPath } from './field-path';
7
8
  export { SubscriptionManager } from './subscription-manager';
8
9
  export { getCurrentValidationRegistry, getCurrentBehaviorRegistry } from './registry-helpers';
9
10
  export { RegistryStack } from './registry-stack';
@@ -1,43 +1,7 @@
1
1
  /**
2
2
  * FieldPath proxy - типобезопасный доступ к путям полей формы
3
- */
4
- import type { FieldPath, FieldPathNode } from '../types';
5
- /**
6
- * Создать FieldPath proxy для формы
7
3
  *
8
- * @example
9
- * ```typescript
10
- * const path = createFieldPath<MyForm>();
11
- * console.log(path.email.__path); // 'email'
12
- * console.log(path.personalData.firstName.__path); // 'personalData.firstName'
13
- * ```
14
- */
15
- export declare function createFieldPath<T>(): FieldPath<T>;
16
- /**
17
- * Извлечь путь из FieldPathNode
18
- */
19
- export declare function extractPath(node: FieldPathNode<unknown, unknown> | unknown): string;
20
- /**
21
- * Преобразовать FieldPathNode в FieldPath для переиспользования схем
22
- *
23
- * Позволяет композировать validation schemas:
24
- *
25
- * @example
26
- * ```typescript
27
- * const personalDataValidation = (path: FieldPath<PersonalData>) => {
28
- * required(path.firstName, { message: 'Имя обязательно' });
29
- * required(path.lastName, { message: 'Фамилия обязательна' });
30
- * };
31
- *
32
- * const mainValidation = (path: FieldPath<MyForm>) => {
33
- * // Переиспользуем схему
34
- * personalDataValidation(toFieldPath(path.personalData));
35
- * required(path.email);
36
- * };
37
- * ```
38
- */
39
- export declare function toFieldPath<T>(node: FieldPathNode<unknown, T, never> | FieldPathNode<any, T, any>): FieldPath<T>;
40
- /**
41
- * Извлечь ключ поля из FieldPathNode
4
+ * @deprecated Импортируйте из '../utils/field-path' напрямую.
5
+ * Этот файл оставлен для обратной совместимости.
42
6
  */
43
- export declare function extractKey(node: FieldPathNode<unknown, unknown> | unknown): string;
7
+ export { createFieldPath, extractPath, extractKey, toFieldPath } from '../utils/field-path';
@@ -45,3 +45,26 @@ export declare class TreeValidationContextImpl<TForm> implements FormContext<TFo
45
45
  */
46
46
  setFieldValue(path: string, value: unknown): void;
47
47
  }
48
+ /**
49
+ * Реализация контекста валидации для ArrayNode
50
+ * Позволяет валидаторам типа notEmpty работать с массивами
51
+ */
52
+ export declare class ArrayValidationContextImpl<TForm, TItem> implements FormContext<TForm> {
53
+ private _form;
54
+ private arrayValue;
55
+ /**
56
+ * Форма с типизированным Proxy-доступом к полям
57
+ */
58
+ readonly form: GroupNodeWithControls<TForm>;
59
+ constructor(form: GroupNode<TForm>, _fieldKey: keyof TForm, arrayValue: TItem[]);
60
+ /**
61
+ * Получить текущее значение массива (для валидатора)
62
+ * @internal
63
+ */
64
+ value(): TItem[];
65
+ /**
66
+ * Безопасно установить значение поля по строковому пути
67
+ * Автоматически использует emitEvent: false для предотвращения циклов
68
+ */
69
+ setFieldValue(path: string, value: unknown): void;
70
+ }