@reformer/core 3.0.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) 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 +26 -19
  18. package/dist/core/behavior/behavior-context.d.ts +26 -12
  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 +39 -19
  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 +25 -21
  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 +26 -26
  39. package/dist/core/types/form-proxy.d.ts +2 -32
  40. package/dist/core/types/index.d.ts +51 -5
  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 +1 -18
  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/utils/field-path.d.ts +23 -11
  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 +9 -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 +58 -42
  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 +1 -1
  90. package/dist/hooks/useArrayLength.d.ts +31 -0
  91. package/dist/hooks/useFormControl.d.ts +4 -4
  92. package/dist/hooks/useFormControlValue.d.ts +2 -2
  93. package/dist/hooks/useHiddenCondition.d.ts +25 -0
  94. package/dist/hooks/useSignalSubscription.d.ts +1 -1
  95. package/dist/index-D25LsbRm.js +73 -0
  96. package/dist/index.d.ts +2 -0
  97. package/dist/index.js +1171 -786
  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 +54 -29
  137. package/llms.txt +7878 -311
  138. package/package.json +83 -9
  139. package/dist/behaviors-DzYL8kY_.js +0 -499
  140. package/dist/core/behavior/create-field-path.d.ts +0 -7
  141. package/dist/core/context/form-context-impl.d.ts +0 -29
  142. package/dist/core/utils/debounce.d.ts +0 -160
  143. package/dist/core/utils/resources.d.ts +0 -41
  144. package/dist/core/validation/field-path.d.ts +0 -7
  145. package/dist/core/validation/validators/date.d.ts +0 -38
  146. package/dist/registry-helpers-BRxAr6nG.js +0 -490
  147. package/dist/validators-gXoHPdqM.js +0 -418
@@ -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
@@ -65,9 +103,6 @@ export type { ContextualValidatorFn, ContextualAsyncValidatorFn, TreeValidatorFn
65
103
  export type { FormSchema, ArrayConfig } from './deep-schema';
66
104
  export type { FormContext } from './form-context';
67
105
  export type { FormControlsProxy, FormProxy, FormArrayProxy } from './form-proxy';
68
- import type { BehaviorSchemaFn } from '../behavior/types';
69
- import type { FormSchema } from './deep-schema';
70
- import type { ValidationSchemaFn } from './validation-schema';
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
@@ -1,15 +1,6 @@
1
- /**
2
- * Типы для validation schema паттерна
3
- *
4
- * Основано на Angular Signal Forms подходе:
5
- * - Валидация определяется отдельно от схемы полей
6
- * - Поддержка условной валидации (applyWhen)
7
- * - Cross-field валидация (validateTree)
8
- * - Асинхронная валидация с контекстом
9
- */
10
- import type { FormFields, ValidationError } from './index';
11
- import type { FieldPath } from './field-path';
12
- import type { FormContext } from './form-context';
1
+ import { FormFields, ValidationError } from './index';
2
+ import { FieldPath } from './field-path';
3
+ import { FormContext } from './form-context';
13
4
  /**
14
5
  * Функция валидации поля с контекстом
15
6
  *
@@ -0,0 +1,74 @@
1
+ import { RegistryStack } from './registry-stack';
2
+ /**
3
+ * Базовый класс для реестров (BehaviorRegistry, ValidationRegistry).
4
+ *
5
+ * Реализует паттерн Template Method для управления регистрацией:
6
+ * `beginRegistration()` → `onBeginRegistration()`, `endRegistration()` → `onEndRegistration()`.
7
+ *
8
+ * @typeParam TRegistration - Тип регистрируемых элементов.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { AbstractRegistry } from '@reformer/core';
13
+ *
14
+ * class MyRegistry extends AbstractRegistry<{ name: string }> {
15
+ * protected onEndRegistration(items: { name: string }[]) {
16
+ * console.log('Registered', items.length);
17
+ * }
18
+ * }
19
+ * ```
20
+ */
21
+ export declare abstract class AbstractRegistry<TRegistration> {
22
+ /** Флаг активной регистрации */
23
+ protected isRegistering: boolean;
24
+ /** Массив зарегистрированных элементов */
25
+ protected registrations: TRegistration[];
26
+ /**
27
+ * Получить стек для конкретного класса реестра
28
+ * Создает новый стек если не существует
29
+ *
30
+ * @param ctor - Конструктор класса реестра
31
+ * @returns RegistryStack для данного класса
32
+ */
33
+ protected static getStack<T extends AbstractRegistry<any>>(ctor: new (...args: any[]) => T): RegistryStack<T>;
34
+ /**
35
+ * Получить текущий активный реестр из стека
36
+ * Должен быть переопределен в наследниках как static метод
37
+ *
38
+ * @param ctor - Конструктор класса реестра
39
+ * @returns Текущий активный реестр или null
40
+ */
41
+ protected static getCurrentFromStack<T extends AbstractRegistry<any>>(ctor: new (...args: any[]) => T): T | null;
42
+ /**
43
+ * Начать регистрацию
44
+ *
45
+ * Помещает this в global stack для изоляции форм
46
+ * Вызывает hook onBeginRegistration()
47
+ */
48
+ beginRegistration(): void;
49
+ /**
50
+ * Проверить, активна ли регистрация
51
+ */
52
+ isActive(): boolean;
53
+ /**
54
+ * Получить зарегистрированные элементы
55
+ */
56
+ getRegistrations(): TRegistration[];
57
+ /**
58
+ * Hook: вызывается в начале регистрации
59
+ * Может быть переопределен в наследниках для инициализации
60
+ */
61
+ protected onBeginRegistration(): void;
62
+ /**
63
+ * Завершить регистрацию и извлечь из стека
64
+ *
65
+ * @param registryName - Имя реестра для отладки
66
+ */
67
+ protected completeRegistration(registryName: string): void;
68
+ /**
69
+ * Отменить регистрацию без применения
70
+ *
71
+ * @param registryName - Имя реестра для отладки
72
+ */
73
+ cancelRegistration(registryName: string): void;
74
+ }
@@ -0,0 +1,71 @@
1
+ import { ReadonlySignal, Signal } from '@preact/signals-core';
2
+ import { FieldStatus, ValidationError } from '../types';
3
+ import { FormNode } from '../nodes/form-node';
4
+ /**
5
+ * Функция получения дочерних узлов
6
+ * Возвращает массив FormNode для агрегации состояния
7
+ */
8
+ type GetChildrenFn<T> = () => FormNode<T>[];
9
+ /**
10
+ * Результат создания агрегированных сигналов
11
+ */
12
+ export interface AggregateSignals {
13
+ /** Все дочерние узлы валидны и нет собственных ошибок */
14
+ valid: ReadonlySignal<boolean>;
15
+ /** Есть ошибки (инверсия valid) */
16
+ invalid: ReadonlySignal<boolean>;
17
+ /** Хотя бы один дочерний узел в состоянии pending */
18
+ pending: ReadonlySignal<boolean>;
19
+ /** Хотя бы один дочерний узел touched */
20
+ touched: ReadonlySignal<boolean>;
21
+ /** Хотя бы один дочерний узел dirty */
22
+ dirty: ReadonlySignal<boolean>;
23
+ /** Собственные ошибки + ошибки всех дочерних узлов */
24
+ errors: ReadonlySignal<ValidationError[]>;
25
+ /** Статус: disabled > pending > invalid > valid */
26
+ status: ReadonlySignal<FieldStatus>;
27
+ }
28
+ /**
29
+ * Опции для создания агрегированных сигналов
30
+ */
31
+ export interface AggregateSignalsOptions<T> {
32
+ /** Функция получения дочерних узлов */
33
+ getChildren: GetChildrenFn<T>;
34
+ /** Signal с собственными ошибками контейнера (form-level или array-level) */
35
+ ownErrors: Signal<ValidationError[]>;
36
+ /** Опциональный signal disabled состояния (для GroupNode) */
37
+ disabled?: Signal<boolean>;
38
+ }
39
+ /**
40
+ * Создать агрегированные computed signals для контейнерного узла
41
+ *
42
+ * Используется в GroupNode и ArrayNode для унификации логики
43
+ * вычисления состояния на основе дочерних узлов.
44
+ *
45
+ * @param options - Опции конфигурации
46
+ * @returns Объект с computed signals
47
+ *
48
+ * @example GroupNode
49
+ * ```typescript
50
+ * const signals = createAggregateSignals({
51
+ * getChildren: () => Array.from(this._fields.values()),
52
+ * ownErrors: this._formErrors,
53
+ * disabled: this._disabled,
54
+ * });
55
+ * this.valid = signals.valid;
56
+ * this.invalid = signals.invalid;
57
+ * // ...
58
+ * ```
59
+ *
60
+ * @example ArrayNode
61
+ * ```typescript
62
+ * const signals = createAggregateSignals({
63
+ * getChildren: () => this.items.value,
64
+ * ownErrors: this._arrayErrors,
65
+ * });
66
+ * this.valid = signals.valid;
67
+ * // ...
68
+ * ```
69
+ */
70
+ export declare function createAggregateSignals<T>(options: AggregateSignalsOptions<T>): AggregateSignals;
71
+ export {};
@@ -1,21 +1,4 @@
1
- /**
2
- * Фабричная функция для создания формы с правильной типизацией
3
- *
4
- * Решает проблему с типизацией конструктора GroupNode, который возвращает
5
- * Proxy (FormProxy), но TypeScript не может это вывести автоматически.
6
- *
7
- * @group Utilities
8
- *
9
- * @example
10
- * ```typescript
11
- * // Вместо:
12
- * const form: FormProxy<MyForm> = new GroupNode<MyForm>(config);
13
- *
14
- * // Используйте:
15
- * const form = createForm<MyForm>(config);
16
- * ```
17
- */
18
- import type { FormProxy, GroupNodeConfig, FormSchema } from '../types';
1
+ import { FormProxy, GroupNodeConfig, FormSchema } from '../types';
19
2
  /**
20
3
  * Создать форму с полной конфигурацией (form, behavior, validation)
21
4
  *
@@ -1,21 +1,4 @@
1
- /**
2
- * FormErrorHandler - централизованная обработка ошибок в формах
3
- *
4
- * Устраняет несогласованность обработки ошибок между:
5
- * - field-node.ts (логирует и конвертирует в ValidationError)
6
- * - behavior-applicator.ts (логирует и пробрасывает)
7
- * - validation-applicator.ts (логирует и проглатывает)
8
- *
9
- * @example
10
- * ```typescript
11
- * try {
12
- * await validator(value);
13
- * } catch (error) {
14
- * return FormErrorHandler.handle(error, 'AsyncValidator', ErrorStrategy.CONVERT);
15
- * }
16
- * ```
17
- */
18
- import type { ValidationError } from '../types';
1
+ import { ValidationError } from '../types';
19
2
  /**
20
3
  * Стратегия обработки ошибок
21
4
  *
@@ -1,4 +1,4 @@
1
- import type { UnknownRecord } from '../types';
1
+ import { UnknownRecord } from '../types';
2
2
  /**
3
3
  * Сегмент пути к полю формы
4
4
  *
@@ -1,12 +1,4 @@
1
- /**
2
- * FieldPath proxy - типобезопасный доступ к путям полей формы
3
- *
4
- * Единый модуль для validation и behavior схем.
5
- * Предоставляет типизированную навигацию по структуре формы.
6
- *
7
- * @module utils/field-path
8
- */
9
- import type { FieldPath, FieldPathNode } from '../types';
1
+ import { FieldPath, FieldPathNode } from '../types';
10
2
  /**
11
3
  * Создать FieldPath proxy для формы
12
4
  *
@@ -19,7 +11,17 @@ import type { FieldPath, FieldPathNode } from '../types';
19
11
  */
20
12
  export declare function createFieldPath<T>(): FieldPath<T>;
21
13
  /**
22
- * Извлечь путь из FieldPathNode
14
+ * Извлечь строковый путь из {@link FieldPathNode}.
15
+ *
16
+ * @param node - Узел `FieldPathNode` либо строка-путь.
17
+ * @returns Путь вида `"a.b.c"`.
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * import { extractPath } from '@reformer/core';
22
+ *
23
+ * const path = (renderPath) => extractPath(renderPath.user.email); // → 'user.email'
24
+ * ```
23
25
  */
24
26
  export declare function extractPath(node: FieldPathNode<unknown, unknown> | unknown): string;
25
27
  /**
@@ -43,6 +45,16 @@ export declare function extractPath(node: FieldPathNode<unknown, unknown> | unkn
43
45
  */
44
46
  export declare function toFieldPath<T>(node: FieldPathNode<unknown, T, never> | FieldPathNode<any, T, any>): FieldPath<T>;
45
47
  /**
46
- * Извлечь ключ поля из FieldPathNode
48
+ * Извлечь имя последнего сегмента ({@link FieldPathSegment}) пути.
49
+ *
50
+ * @param node - Узел `FieldPathNode` либо строка-путь.
51
+ * @returns Имя поля без префикса родителя (последний сегмент).
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * import { extractKey } from '@reformer/core';
56
+ *
57
+ * extractKey(path.user.email); // → 'email'
58
+ * ```
47
59
  */
48
60
  export declare function extractKey(node: FieldPathNode<unknown, unknown> | unknown): string;
@@ -0,0 +1,176 @@
1
+ import { FormFields, FormValue, FieldStatus, ValidationError } from '../types';
2
+ /**
3
+ * Тип события изменения в форме
4
+ */
5
+ export type FormChangeType = 'value' | 'status' | 'errors' | 'touched' | 'dirty';
6
+ /**
7
+ * Событие изменения в форме
8
+ */
9
+ export interface FormChangeEvent {
10
+ /** Тип изменения */
11
+ type: FormChangeType;
12
+ /** Путь к полю */
13
+ path: string;
14
+ /** Timestamp события */
15
+ timestamp: number;
16
+ /** Старое значение */
17
+ oldValue?: unknown;
18
+ /** Новое значение */
19
+ newValue: unknown;
20
+ }
21
+ /**
22
+ * Интерфейс узла формы для observer
23
+ */
24
+ export interface ObservableFormNode {
25
+ value: {
26
+ value: FormValue;
27
+ };
28
+ status: {
29
+ value: FieldStatus;
30
+ };
31
+ errors: {
32
+ value: ValidationError[];
33
+ };
34
+ touched: {
35
+ value: boolean;
36
+ };
37
+ dirty: {
38
+ value: boolean;
39
+ };
40
+ }
41
+ /**
42
+ * Интерфейс формы для observer
43
+ */
44
+ export interface ObservableForm<T extends FormFields> {
45
+ value: {
46
+ value: T;
47
+ };
48
+ status: {
49
+ value: FieldStatus;
50
+ };
51
+ errors: {
52
+ value: ValidationError[];
53
+ };
54
+ touched: {
55
+ value: boolean;
56
+ };
57
+ dirty: {
58
+ value: boolean;
59
+ };
60
+ getFieldByPath(path: string): ObservableFormNode | undefined;
61
+ }
62
+ /**
63
+ * Callback для обработки событий изменения
64
+ */
65
+ export type FormChangeCallback = (event: FormChangeEvent) => void;
66
+ /**
67
+ * Опции для FormObserver
68
+ */
69
+ export interface FormObserverOptions {
70
+ /** Включить логирование в консоль (по умолчанию true в DEV) */
71
+ enableLogging?: boolean;
72
+ /** Фильтр типов событий */
73
+ eventTypes?: FormChangeType[];
74
+ /** Фильтр путей полей (regex или массив) */
75
+ pathFilter?: RegExp | string[];
76
+ }
77
+ /**
78
+ * FormObserver - мониторинг изменений в форме
79
+ *
80
+ * Полезен для:
81
+ * - Отладки сложных форм
82
+ * - Логирования изменений для аудита
83
+ * - Синхронизации с внешними системами
84
+ *
85
+ * @example
86
+ * ```typescript
87
+ * const observer = new FormObserver(form, {
88
+ * enableLogging: true,
89
+ * eventTypes: ['value', 'errors']
90
+ * });
91
+ *
92
+ * // Подписка на события
93
+ * const unsubscribe = observer.subscribe((event) => {
94
+ * console.log(`${event.type} at ${event.path}:`, event.newValue);
95
+ * });
96
+ *
97
+ * // Включить трассировку
98
+ * const disposeTracing = observer.enableTracing();
99
+ *
100
+ * // Cleanup
101
+ * unsubscribe();
102
+ * disposeTracing();
103
+ * ```
104
+ */
105
+ export declare class FormObserver<T extends FormFields> {
106
+ private readonly form;
107
+ private listeners;
108
+ private disposers;
109
+ private options;
110
+ /**
111
+ * @param form - Форма для наблюдения
112
+ * @param options - Опции observer
113
+ */
114
+ constructor(form: ObservableForm<T>, options?: FormObserverOptions);
115
+ /**
116
+ * Подписаться на события изменения
117
+ *
118
+ * @param callback - Функция обработки события
119
+ * @returns Функция отписки
120
+ *
121
+ * @example
122
+ * ```typescript
123
+ * const unsubscribe = observer.subscribe((event) => {
124
+ * // Отправить событие в analytics
125
+ * analytics.track('form_change', event);
126
+ * });
127
+ * ```
128
+ */
129
+ subscribe(callback: FormChangeCallback): () => void;
130
+ /**
131
+ * Включить трассировку формы
132
+ *
133
+ * Подписывается на изменения основных сигналов формы
134
+ * и вызывает listeners при каждом изменении
135
+ *
136
+ * @returns Функция для отключения трассировки
137
+ *
138
+ * @example
139
+ * ```typescript
140
+ * const dispose = observer.enableTracing();
141
+ *
142
+ * // Позже, для отключения
143
+ * dispose();
144
+ * ```
145
+ */
146
+ enableTracing(): () => void;
147
+ /**
148
+ * Наблюдать за конкретным полем
149
+ *
150
+ * @param path - Путь к полю
151
+ * @returns Функция для отключения наблюдения
152
+ *
153
+ * @example
154
+ * ```typescript
155
+ * // Наблюдать за полем email
156
+ * const dispose = observer.watchField('email');
157
+ * ```
158
+ */
159
+ watchField(path: string): () => void;
160
+ /**
161
+ * Отправить событие всем listeners
162
+ */
163
+ private emit;
164
+ /**
165
+ * Проверить, нужно ли отслеживать тип события
166
+ */
167
+ private shouldTrack;
168
+ /**
169
+ * Проверить, соответствует ли путь фильтру
170
+ */
171
+ private matchesPathFilter;
172
+ /**
173
+ * Очистить все подписки и disposers
174
+ */
175
+ dispose(): void;
176
+ }
@@ -0,0 +1,25 @@
1
+ import { FormNode } from '../nodes/form-node';
2
+ import { FormProxy } from '../types/form-proxy';
3
+ /**
4
+ * Создать Proxy для типобезопасного доступа к полям GroupNode
5
+ *
6
+ * Proxy обеспечивает:
7
+ * - Доступ к полям через точечную нотацию (form.email, form.address.city)
8
+ * - Приоритет собственных свойств GroupNode над полями
9
+ * - Цепочку proxy для вложенных GroupNode
10
+ * - Блокировку прямого присваивания полям (только через setValue/patchValue)
11
+ *
12
+ * @param target - GroupNode для которого создаётся proxy
13
+ * @param fields - Map полей формы
14
+ * @returns Типизированный Proxy
15
+ *
16
+ * @internal
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * const proxy = buildFormProxy(groupNode, groupNode._fields);
21
+ * proxy.email.setValue('test@example.com');
22
+ * console.log(proxy.email.value.value);
23
+ * ```
24
+ */
25
+ export declare function buildFormProxy<T>(target: FormNode<T>, fields: Map<keyof T, FormNode<unknown>>): FormProxy<T>;
@@ -0,0 +1,121 @@
1
+ import { ReadonlySignal } from '@preact/signals-core';
2
+ import { FormFields } from '../types';
3
+ /**
4
+ * Интерфейс формы для FormSubmitter
5
+ * Минимальный контракт для работы с любой формой
6
+ */
7
+ export interface SubmittableForm<T extends FormFields> {
8
+ /** Пометить все поля как touched */
9
+ markAsTouched(): void;
10
+ /** Валидировать форму */
11
+ validate(): Promise<boolean>;
12
+ /** Получить значения формы */
13
+ getValue(): T;
14
+ }
15
+ /**
16
+ * Опции для submit
17
+ */
18
+ export interface SubmitOptions {
19
+ /** Пропустить валидацию перед submit */
20
+ skipValidation?: boolean;
21
+ /** Пропустить markAsTouched перед submit */
22
+ skipTouch?: boolean;
23
+ }
24
+ /**
25
+ * Результат submit
26
+ */
27
+ export interface SubmitResult<R> {
28
+ /** Успешно ли выполнен submit */
29
+ success: boolean;
30
+ /** Результат от onSubmit callback */
31
+ data: R | null;
32
+ /** Ошибка, если submit не удался */
33
+ error?: Error;
34
+ }
35
+ /**
36
+ * FormSubmitter - управляет процессом отправки формы
37
+ *
38
+ * @example
39
+ * ```typescript
40
+ * const submitter = new FormSubmitter(form);
41
+ *
42
+ * // Простой submit
43
+ * const result = await submitter.submit(async (values) => {
44
+ * return await api.saveForm(values);
45
+ * });
46
+ *
47
+ * // Проверка состояния
48
+ * if (submitter.submitting.value) {
49
+ * console.log('Форма отправляется...');
50
+ * }
51
+ * ```
52
+ */
53
+ export declare class FormSubmitter<T extends FormFields> {
54
+ private readonly form;
55
+ /** Внутренний сигнал состояния отправки */
56
+ private readonly _submitting;
57
+ /** Публичный read-only сигнал состояния отправки */
58
+ readonly submitting: ReadonlySignal<boolean>;
59
+ /**
60
+ * @param form - Форма для отправки
61
+ */
62
+ constructor(form: SubmittableForm<T>);
63
+ /**
64
+ * Отправить форму
65
+ *
66
+ * Процесс:
67
+ * 1. Помечает все поля как touched (для отображения ошибок)
68
+ * 2. Валидирует форму
69
+ * 3. Если валидация успешна - вызывает onSubmit
70
+ * 4. Управляет состоянием submitting
71
+ *
72
+ * @param onSubmit - Callback для отправки данных
73
+ * @param options - Опции submit
74
+ * @returns Результат от onSubmit или null если валидация не пройдена
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * const result = await submitter.submit(async (values) => {
79
+ * const response = await fetch('/api/form', {
80
+ * method: 'POST',
81
+ * body: JSON.stringify(values)
82
+ * });
83
+ * return response.json();
84
+ * });
85
+ *
86
+ * if (result === null) {
87
+ * console.log('Форма не прошла валидацию');
88
+ * }
89
+ * ```
90
+ */
91
+ submit<R>(onSubmit: (values: T) => Promise<R> | R, options?: SubmitOptions): Promise<R | null>;
92
+ /**
93
+ * Отправить форму с расширенным результатом
94
+ *
95
+ * В отличие от submit(), возвращает объект с информацией об успехе/ошибке
96
+ *
97
+ * @param onSubmit - Callback для отправки данных
98
+ * @param options - Опции submit
99
+ * @returns Объект SubmitResult с данными и статусом
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * const result = await submitter.submitWithResult(async (values) => {
104
+ * return await api.saveForm(values);
105
+ * });
106
+ *
107
+ * if (result.success) {
108
+ * console.log('Сохранено:', result.data);
109
+ * } else if (result.error) {
110
+ * console.error('Ошибка:', result.error.message);
111
+ * } else {
112
+ * console.log('Валидация не пройдена');
113
+ * }
114
+ * ```
115
+ */
116
+ submitWithResult<R>(onSubmit: (values: T) => Promise<R> | R, options?: SubmitOptions): Promise<SubmitResult<R>>;
117
+ /**
118
+ * Проверить, идет ли отправка формы
119
+ */
120
+ isSubmitting(): boolean;
121
+ }