@reformer/core 1.0.0-beta.3

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 (150) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +53 -0
  3. package/dist/behaviors.d.ts +2 -0
  4. package/dist/behaviors.js +230 -0
  5. package/dist/core/behavior/behavior-applicator.d.ts +71 -0
  6. package/dist/core/behavior/behavior-applicator.js +92 -0
  7. package/dist/core/behavior/behavior-context.d.ts +29 -0
  8. package/dist/core/behavior/behavior-context.js +38 -0
  9. package/dist/core/behavior/behavior-registry.d.ts +97 -0
  10. package/dist/core/behavior/behavior-registry.js +198 -0
  11. package/dist/core/behavior/behaviors/compute-from.d.ts +41 -0
  12. package/dist/core/behavior/behaviors/compute-from.js +84 -0
  13. package/dist/core/behavior/behaviors/copy-from.d.ts +31 -0
  14. package/dist/core/behavior/behaviors/copy-from.js +64 -0
  15. package/dist/core/behavior/behaviors/enable-when.d.ts +49 -0
  16. package/dist/core/behavior/behaviors/enable-when.js +81 -0
  17. package/dist/core/behavior/behaviors/index.d.ts +11 -0
  18. package/dist/core/behavior/behaviors/index.js +11 -0
  19. package/dist/core/behavior/behaviors/reset-when.d.ts +51 -0
  20. package/dist/core/behavior/behaviors/reset-when.js +63 -0
  21. package/dist/core/behavior/behaviors/revalidate-when.d.ts +30 -0
  22. package/dist/core/behavior/behaviors/revalidate-when.js +51 -0
  23. package/dist/core/behavior/behaviors/sync-fields.d.ts +28 -0
  24. package/dist/core/behavior/behaviors/sync-fields.js +66 -0
  25. package/dist/core/behavior/behaviors/transform-value.d.ts +120 -0
  26. package/dist/core/behavior/behaviors/transform-value.js +110 -0
  27. package/dist/core/behavior/behaviors/watch-field.d.ts +35 -0
  28. package/dist/core/behavior/behaviors/watch-field.js +56 -0
  29. package/dist/core/behavior/compose-behavior.d.ts +106 -0
  30. package/dist/core/behavior/compose-behavior.js +166 -0
  31. package/dist/core/behavior/create-field-path.d.ts +20 -0
  32. package/dist/core/behavior/create-field-path.js +69 -0
  33. package/dist/core/behavior/index.d.ts +12 -0
  34. package/dist/core/behavior/index.js +17 -0
  35. package/dist/core/behavior/types.d.ts +152 -0
  36. package/dist/core/behavior/types.js +7 -0
  37. package/dist/core/context/form-context-impl.d.ts +29 -0
  38. package/dist/core/context/form-context-impl.js +37 -0
  39. package/dist/core/factories/index.d.ts +6 -0
  40. package/dist/core/factories/index.js +6 -0
  41. package/dist/core/factories/node-factory.d.ts +209 -0
  42. package/dist/core/factories/node-factory.js +281 -0
  43. package/dist/core/nodes/array-node.d.ts +308 -0
  44. package/dist/core/nodes/array-node.js +534 -0
  45. package/dist/core/nodes/field-node.d.ts +269 -0
  46. package/dist/core/nodes/field-node.js +510 -0
  47. package/dist/core/nodes/form-node.d.ts +342 -0
  48. package/dist/core/nodes/form-node.js +343 -0
  49. package/dist/core/nodes/group-node/field-registry.d.ts +191 -0
  50. package/dist/core/nodes/group-node/field-registry.js +215 -0
  51. package/dist/core/nodes/group-node/index.d.ts +11 -0
  52. package/dist/core/nodes/group-node/index.js +11 -0
  53. package/dist/core/nodes/group-node/proxy-builder.d.ts +71 -0
  54. package/dist/core/nodes/group-node/proxy-builder.js +161 -0
  55. package/dist/core/nodes/group-node/state-manager.d.ts +184 -0
  56. package/dist/core/nodes/group-node/state-manager.js +265 -0
  57. package/dist/core/nodes/group-node.d.ts +494 -0
  58. package/dist/core/nodes/group-node.js +770 -0
  59. package/dist/core/types/deep-schema.d.ts +78 -0
  60. package/dist/core/types/deep-schema.js +11 -0
  61. package/dist/core/types/field-path.d.ts +42 -0
  62. package/dist/core/types/field-path.js +4 -0
  63. package/dist/core/types/form-context.d.ts +83 -0
  64. package/dist/core/types/form-context.js +25 -0
  65. package/dist/core/types/group-node-proxy.d.ts +135 -0
  66. package/dist/core/types/group-node-proxy.js +31 -0
  67. package/dist/core/types/index.d.ts +163 -0
  68. package/dist/core/types/index.js +4 -0
  69. package/dist/core/types/validation-schema.d.ts +104 -0
  70. package/dist/core/types/validation-schema.js +10 -0
  71. package/dist/core/utils/create-form.d.ts +61 -0
  72. package/dist/core/utils/create-form.js +24 -0
  73. package/dist/core/utils/debounce.d.ts +160 -0
  74. package/dist/core/utils/debounce.js +197 -0
  75. package/dist/core/utils/error-handler.d.ts +180 -0
  76. package/dist/core/utils/error-handler.js +226 -0
  77. package/dist/core/utils/field-path-navigator.d.ts +240 -0
  78. package/dist/core/utils/field-path-navigator.js +374 -0
  79. package/dist/core/utils/index.d.ts +14 -0
  80. package/dist/core/utils/index.js +14 -0
  81. package/dist/core/utils/registry-helpers.d.ts +50 -0
  82. package/dist/core/utils/registry-helpers.js +79 -0
  83. package/dist/core/utils/registry-stack.d.ts +69 -0
  84. package/dist/core/utils/registry-stack.js +86 -0
  85. package/dist/core/utils/resources.d.ts +41 -0
  86. package/dist/core/utils/resources.js +69 -0
  87. package/dist/core/utils/subscription-manager.d.ts +180 -0
  88. package/dist/core/utils/subscription-manager.js +214 -0
  89. package/dist/core/utils/type-guards.d.ts +116 -0
  90. package/dist/core/utils/type-guards.js +169 -0
  91. package/dist/core/validation/core/apply-when.d.ts +28 -0
  92. package/dist/core/validation/core/apply-when.js +41 -0
  93. package/dist/core/validation/core/apply.d.ts +63 -0
  94. package/dist/core/validation/core/apply.js +38 -0
  95. package/dist/core/validation/core/index.d.ts +8 -0
  96. package/dist/core/validation/core/index.js +8 -0
  97. package/dist/core/validation/core/validate-async.d.ts +42 -0
  98. package/dist/core/validation/core/validate-async.js +45 -0
  99. package/dist/core/validation/core/validate-tree.d.ts +35 -0
  100. package/dist/core/validation/core/validate-tree.js +37 -0
  101. package/dist/core/validation/core/validate.d.ts +32 -0
  102. package/dist/core/validation/core/validate.js +38 -0
  103. package/dist/core/validation/field-path.d.ts +43 -0
  104. package/dist/core/validation/field-path.js +147 -0
  105. package/dist/core/validation/index.d.ts +21 -0
  106. package/dist/core/validation/index.js +33 -0
  107. package/dist/core/validation/validate-form.d.ts +85 -0
  108. package/dist/core/validation/validate-form.js +152 -0
  109. package/dist/core/validation/validation-applicator.d.ts +89 -0
  110. package/dist/core/validation/validation-applicator.js +217 -0
  111. package/dist/core/validation/validation-context.d.ts +47 -0
  112. package/dist/core/validation/validation-context.js +75 -0
  113. package/dist/core/validation/validation-registry.d.ts +156 -0
  114. package/dist/core/validation/validation-registry.js +298 -0
  115. package/dist/core/validation/validators/array-validators.d.ts +63 -0
  116. package/dist/core/validation/validators/array-validators.js +86 -0
  117. package/dist/core/validation/validators/date.d.ts +38 -0
  118. package/dist/core/validation/validators/date.js +117 -0
  119. package/dist/core/validation/validators/email.d.ts +44 -0
  120. package/dist/core/validation/validators/email.js +60 -0
  121. package/dist/core/validation/validators/index.d.ts +14 -0
  122. package/dist/core/validation/validators/index.js +14 -0
  123. package/dist/core/validation/validators/max-length.d.ts +45 -0
  124. package/dist/core/validation/validators/max-length.js +60 -0
  125. package/dist/core/validation/validators/max.d.ts +45 -0
  126. package/dist/core/validation/validators/max.js +60 -0
  127. package/dist/core/validation/validators/min-length.d.ts +45 -0
  128. package/dist/core/validation/validators/min-length.js +60 -0
  129. package/dist/core/validation/validators/min.d.ts +45 -0
  130. package/dist/core/validation/validators/min.js +60 -0
  131. package/dist/core/validation/validators/number.d.ts +38 -0
  132. package/dist/core/validation/validators/number.js +90 -0
  133. package/dist/core/validation/validators/pattern.d.ts +47 -0
  134. package/dist/core/validation/validators/pattern.js +62 -0
  135. package/dist/core/validation/validators/phone.d.ts +34 -0
  136. package/dist/core/validation/validators/phone.js +58 -0
  137. package/dist/core/validation/validators/required.d.ts +48 -0
  138. package/dist/core/validation/validators/required.js +69 -0
  139. package/dist/core/validation/validators/url.d.ts +29 -0
  140. package/dist/core/validation/validators/url.js +55 -0
  141. package/dist/create-field-path-CdPF3lIK.js +704 -0
  142. package/dist/hooks/useFormControl.d.ts +48 -0
  143. package/dist/hooks/useFormControl.js +298 -0
  144. package/dist/index.d.ts +10 -0
  145. package/dist/index.js +8 -0
  146. package/dist/node-factory-D7DOnSSN.js +3200 -0
  147. package/dist/validators.d.ts +2 -0
  148. package/dist/validators.js +298 -0
  149. package/llms.txt +847 -0
  150. package/package.json +86 -0
@@ -0,0 +1,147 @@
1
+ /**
2
+ * FieldPath proxy - типобезопасный доступ к путям полей формы
3
+ */
4
+ /**
5
+ * Создать FieldPath proxy для формы
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * const path = createFieldPath<MyForm>();
10
+ * console.log(path.email.__path); // 'email'
11
+ * console.log(path.personalData.firstName.__path); // 'personalData.firstName'
12
+ * ```
13
+ */
14
+ export function createFieldPath() {
15
+ return createFieldPathProxy('');
16
+ }
17
+ /**
18
+ * Внутренняя функция для создания прокси с отслеживанием пути
19
+ * @private
20
+ */
21
+ function createFieldPathProxy(basePath) {
22
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
23
+ return new Proxy({}, {
24
+ get(_target, prop) {
25
+ if (typeof prop === 'symbol') {
26
+ return undefined;
27
+ }
28
+ // Специальные свойства для метаинформации
29
+ if (prop === '__path') {
30
+ return basePath || prop;
31
+ }
32
+ if (prop === '__key') {
33
+ const parts = basePath.split('.');
34
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
35
+ return (parts[parts.length - 1] || prop);
36
+ }
37
+ // Игнорируем некоторые служебные свойства
38
+ if (prop === 'then' ||
39
+ prop === 'catch' ||
40
+ prop === 'finally' ||
41
+ prop === 'constructor' ||
42
+ prop === 'toString' ||
43
+ prop === 'valueOf' ||
44
+ prop === 'toJSON') {
45
+ return undefined;
46
+ }
47
+ // Создаем новый proxy для вложенного свойства
48
+ const newPath = basePath ? `${basePath}.${prop}` : prop;
49
+ // Возвращаем объект, который ведет себя и как значение, и как прокси
50
+ const node = {
51
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
52
+ __key: prop,
53
+ __path: newPath,
54
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
+ __formType: undefined,
56
+ __fieldType: undefined,
57
+ };
58
+ // Создаем прокси, который содержит и метаинформацию, и позволяет дальнейшую навигацию
59
+ return new Proxy(node, {
60
+ get(_nodeTarget, nodeProp) {
61
+ if (typeof nodeProp === 'symbol') {
62
+ return undefined;
63
+ }
64
+ // Если запрашивают метаинформацию, возвращаем её
65
+ if (nodeProp === '__path')
66
+ return newPath;
67
+ if (nodeProp === '__key')
68
+ return prop;
69
+ if (nodeProp === '__formType')
70
+ return undefined;
71
+ if (nodeProp === '__fieldType')
72
+ return undefined;
73
+ // Игнорируем служебные свойства
74
+ if (nodeProp === 'then' ||
75
+ nodeProp === 'catch' ||
76
+ nodeProp === 'finally' ||
77
+ nodeProp === 'constructor' ||
78
+ nodeProp === 'toString' ||
79
+ nodeProp === 'valueOf' ||
80
+ nodeProp === 'toJSON') {
81
+ return undefined;
82
+ }
83
+ // Иначе создаем еще более вложенный proxy
84
+ return createFieldPathProxy(`${newPath}.${nodeProp}`);
85
+ },
86
+ });
87
+ },
88
+ });
89
+ }
90
+ /**
91
+ * Извлечь путь из FieldPathNode
92
+ */
93
+ export function extractPath(node) {
94
+ // Fallback для строк
95
+ if (typeof node === 'string') {
96
+ return node;
97
+ }
98
+ // Проверка для Proxy и обычных объектов
99
+ if (node && typeof node === 'object') {
100
+ // Пытаемся получить __path напрямую (работает для Proxy)
101
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
102
+ const path = node.__path;
103
+ if (typeof path === 'string') {
104
+ return path;
105
+ }
106
+ }
107
+ throw new Error('Invalid field path node: ' + JSON.stringify(node));
108
+ }
109
+ /**
110
+ * Преобразовать FieldPathNode в FieldPath для переиспользования схем
111
+ *
112
+ * Позволяет композировать validation schemas:
113
+ *
114
+ * @example
115
+ * ```typescript
116
+ * const personalDataValidation = (path: FieldPath<PersonalData>) => {
117
+ * required(path.firstName, { message: 'Имя обязательно' });
118
+ * required(path.lastName, { message: 'Фамилия обязательна' });
119
+ * };
120
+ *
121
+ * const mainValidation = (path: FieldPath<MyForm>) => {
122
+ * // Переиспользуем схему
123
+ * personalDataValidation(toFieldPath(path.personalData));
124
+ * required(path.email);
125
+ * };
126
+ * ```
127
+ */
128
+ export function toFieldPath(
129
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
130
+ node) {
131
+ const basePath = extractPath(node);
132
+ return createFieldPathProxy(basePath);
133
+ }
134
+ /**
135
+ * Извлечь ключ поля из FieldPathNode
136
+ */
137
+ export function extractKey(node) {
138
+ if (node && typeof node === 'object' && '__key' in node) {
139
+ return node.__key;
140
+ }
141
+ // Fallback для строк
142
+ if (typeof node === 'string') {
143
+ const parts = node.split('.');
144
+ return parts[parts.length - 1];
145
+ }
146
+ throw new Error('Invalid field path node');
147
+ }
@@ -0,0 +1,21 @@
1
+ export { validate } from './core/validate';
2
+ export { validateAsync } from './core/validate-async';
3
+ export { validateTree } from './core/validate-tree';
4
+ export { apply } from './core/apply';
5
+ export { applyWhen } from './core/apply-when';
6
+ export { required } from './validators/required';
7
+ export { min } from './validators/min';
8
+ export { max } from './validators/max';
9
+ export { minLength } from './validators/min-length';
10
+ export { maxLength } from './validators/max-length';
11
+ export { email } from './validators/email';
12
+ export { pattern } from './validators/pattern';
13
+ export { url } from './validators/url';
14
+ export { phone, type PhoneFormat } from './validators/phone';
15
+ export { number } from './validators/number';
16
+ export { date } from './validators/date';
17
+ export { notEmpty, validateItems } from './validators/array-validators';
18
+ export { createFieldPath, extractPath, extractKey, toFieldPath } from './field-path';
19
+ export { validateForm } from './validate-form';
20
+ export { ValidationRegistry } from './validation-registry';
21
+ export { ValidationContextImpl, TreeValidationContextImpl } from './validation-context';
@@ -0,0 +1,33 @@
1
+ // ============================================================================
2
+ // Validation Schema API
3
+ // ============================================================================
4
+ // Core validation functions
5
+ export { validate } from './core/validate';
6
+ export { validateAsync } from './core/validate-async';
7
+ export { validateTree } from './core/validate-tree';
8
+ export { apply } from './core/apply';
9
+ export { applyWhen } from './core/apply-when';
10
+ // Reusable validators
11
+ export { required } from './validators/required';
12
+ export { min } from './validators/min';
13
+ export { max } from './validators/max';
14
+ export { minLength } from './validators/min-length';
15
+ export { maxLength } from './validators/max-length';
16
+ export { email } from './validators/email';
17
+ export { pattern } from './validators/pattern';
18
+ export { url } from './validators/url';
19
+ export { phone } from './validators/phone';
20
+ export { number } from './validators/number';
21
+ export { date } from './validators/date';
22
+ // Валидаторы для массивов
23
+ export { notEmpty, validateItems } from './validators/array-validators';
24
+ // Утилиты для FieldPath
25
+ export { createFieldPath, extractPath, extractKey, toFieldPath } from './field-path';
26
+ // Утилита для валидации формы по схеме
27
+ export { validateForm } from './validate-form';
28
+ // ValidationRegistry (для внутреннего использования)
29
+ // Примечание: ValidationRegistry (глобальный singleton) был удален в пользу
30
+ // локальных экземпляров ValidationRegistry в каждом GroupNode
31
+ export { ValidationRegistry } from './validation-registry';
32
+ // Контексты валидации
33
+ export { ValidationContextImpl, TreeValidationContextImpl } from './validation-context';
@@ -0,0 +1,85 @@
1
+ /**
2
+ * validateForm - утилита для валидации формы в соответствии со схемой
3
+ *
4
+ * Позволяет применить validation schema к форме без изменения
5
+ * зарегистрированной схемы в ValidationRegistry.
6
+ *
7
+ * ## Использование
8
+ *
9
+ * - **Multi-step формы**: валидация только полей текущего шага
10
+ * - **Условная валидация**: применение разных схем в зависимости от состояния
11
+ * - **Временная валидация**: проверка без сохранения в реестр
12
+ *
13
+ * ## Как это работает
14
+ *
15
+ * 1. Создаётся временный ValidationRegistry для схемы
16
+ * 2. Валидируются все FieldNode (field-level валидаторы из FieldConfig)
17
+ * 3. Применяются ТОЛЬКО contextual validators из переданной схемы
18
+ * 4. Временный реестр отменяется (не сохраняется в форму)
19
+ *
20
+ * ## Важно для multi-step форм
21
+ *
22
+ * Форма может быть создана С полной схемой валидации:
23
+ * ```typescript
24
+ * createForm({
25
+ * form: schema,
26
+ * validation: fullValidation, // Полная схема
27
+ * });
28
+ * ```
29
+ *
30
+ * При этом `validateForm(form, stepSchema)` будет применять
31
+ * только валидаторы из `stepSchema`, игнорируя валидаторы
32
+ * из других шагов.
33
+ *
34
+ * @see docs/multi-step-validation.md для подробной документации
35
+ *
36
+ * @module validation
37
+ */
38
+ import { GroupNode } from '../nodes/group-node';
39
+ import type { ValidationSchemaFn, FormFields } from '../types';
40
+ /**
41
+ * Валидировать форму в соответствии с указанной схемой
42
+ *
43
+ * Функция создает временный контекст валидации, применяет валидаторы
44
+ * из схемы и очищает контекст без сохранения в реестр.
45
+ *
46
+ * @param form - GroupNode для валидации
47
+ * @param schema - Схема валидации (ValidationSchemaFn)
48
+ * @returns Promise<boolean> - `true` если форма валидна, `false` если есть ошибки
49
+ *
50
+ * @example Multi-step форма: валидация текущего шага
51
+ * ```typescript
52
+ * const goToNextStep = async () => {
53
+ * const isValid = await validateForm(form, step1LoanValidation);
54
+ *
55
+ * if (!isValid) {
56
+ * form.markAsTouched(); // Показать ошибки
57
+ * return false;
58
+ * }
59
+ *
60
+ * setCurrentStep(2);
61
+ * return true;
62
+ * };
63
+ * ```
64
+ *
65
+ * @example Полная валидация перед submit
66
+ * ```typescript
67
+ * const handleSubmit = async () => {
68
+ * const isValid = await validateForm(form, fullValidationSchema);
69
+ *
70
+ * if (isValid) {
71
+ * await form.submit(onSubmit);
72
+ * }
73
+ * };
74
+ * ```
75
+ *
76
+ * @example Условная валидация
77
+ * ```typescript
78
+ * const schema = isBusinessAccount
79
+ * ? businessValidation
80
+ * : personalValidation;
81
+ *
82
+ * const isValid = await validateForm(form, schema);
83
+ * ```
84
+ */
85
+ export declare function validateForm<T extends FormFields>(form: GroupNode<T>, schema: ValidationSchemaFn<T>): Promise<boolean>;
@@ -0,0 +1,152 @@
1
+ /**
2
+ * validateForm - утилита для валидации формы в соответствии со схемой
3
+ *
4
+ * Позволяет применить validation schema к форме без изменения
5
+ * зарегистрированной схемы в ValidationRegistry.
6
+ *
7
+ * ## Использование
8
+ *
9
+ * - **Multi-step формы**: валидация только полей текущего шага
10
+ * - **Условная валидация**: применение разных схем в зависимости от состояния
11
+ * - **Временная валидация**: проверка без сохранения в реестр
12
+ *
13
+ * ## Как это работает
14
+ *
15
+ * 1. Создаётся временный ValidationRegistry для схемы
16
+ * 2. Валидируются все FieldNode (field-level валидаторы из FieldConfig)
17
+ * 3. Применяются ТОЛЬКО contextual validators из переданной схемы
18
+ * 4. Временный реестр отменяется (не сохраняется в форму)
19
+ *
20
+ * ## Важно для multi-step форм
21
+ *
22
+ * Форма может быть создана С полной схемой валидации:
23
+ * ```typescript
24
+ * createForm({
25
+ * form: schema,
26
+ * validation: fullValidation, // Полная схема
27
+ * });
28
+ * ```
29
+ *
30
+ * При этом `validateForm(form, stepSchema)` будет применять
31
+ * только валидаторы из `stepSchema`, игнорируя валидаторы
32
+ * из других шагов.
33
+ *
34
+ * @see docs/multi-step-validation.md для подробной документации
35
+ *
36
+ * @module validation
37
+ */
38
+ import { GroupNode } from '../nodes/group-node';
39
+ import { FieldNode } from '../nodes/field-node';
40
+ import { ArrayNode } from '../nodes/array-node';
41
+ import { ValidationRegistry } from './validation-registry';
42
+ import { createFieldPath } from './field-path';
43
+ /**
44
+ * Рекурсивно собирает все FieldNode из дерева формы
45
+ *
46
+ * Пропускает GroupNode и ArrayNode как узлы, но обходит их детей.
47
+ * Это необходимо для validateForm, чтобы избежать триггера
48
+ * contextual validators из validationRegistry вложенных групп.
49
+ *
50
+ * @param node - Корневой узел для обхода
51
+ * @returns Массив всех FieldNode в дереве
52
+ */
53
+ function collectAllFieldNodes(node) {
54
+ if (node instanceof FieldNode) {
55
+ return [node];
56
+ }
57
+ if (node instanceof GroupNode) {
58
+ return Array.from(node.getAllFields()).flatMap(collectAllFieldNodes);
59
+ }
60
+ if (node instanceof ArrayNode) {
61
+ // items приватный, используем публичный метод map()
62
+ return node.map((item) => collectAllFieldNodes(item)).flat();
63
+ }
64
+ return [];
65
+ }
66
+ /**
67
+ * Валидировать форму в соответствии с указанной схемой
68
+ *
69
+ * Функция создает временный контекст валидации, применяет валидаторы
70
+ * из схемы и очищает контекст без сохранения в реестр.
71
+ *
72
+ * @param form - GroupNode для валидации
73
+ * @param schema - Схема валидации (ValidationSchemaFn)
74
+ * @returns Promise<boolean> - `true` если форма валидна, `false` если есть ошибки
75
+ *
76
+ * @example Multi-step форма: валидация текущего шага
77
+ * ```typescript
78
+ * const goToNextStep = async () => {
79
+ * const isValid = await validateForm(form, step1LoanValidation);
80
+ *
81
+ * if (!isValid) {
82
+ * form.markAsTouched(); // Показать ошибки
83
+ * return false;
84
+ * }
85
+ *
86
+ * setCurrentStep(2);
87
+ * return true;
88
+ * };
89
+ * ```
90
+ *
91
+ * @example Полная валидация перед submit
92
+ * ```typescript
93
+ * const handleSubmit = async () => {
94
+ * const isValid = await validateForm(form, fullValidationSchema);
95
+ *
96
+ * if (isValid) {
97
+ * await form.submit(onSubmit);
98
+ * }
99
+ * };
100
+ * ```
101
+ *
102
+ * @example Условная валидация
103
+ * ```typescript
104
+ * const schema = isBusinessAccount
105
+ * ? businessValidation
106
+ * : personalValidation;
107
+ *
108
+ * const isValid = await validateForm(form, schema);
109
+ * ```
110
+ */
111
+ export async function validateForm(form, schema) {
112
+ // Создаем временный реестр для этой валидации
113
+ // Это изолирует валидацию от других форм и не затрагивает постоянный реестр формы
114
+ const tempRegistry = new ValidationRegistry();
115
+ // Начинаем регистрацию валидаторов в временном реестре
116
+ tempRegistry.beginRegistration();
117
+ let tempValidators = [];
118
+ let cancelled = false;
119
+ try {
120
+ // Регистрируем валидаторы из схемы
121
+ // schema() будет использовать tempRegistry через getCurrent() из context stack
122
+ const path = createFieldPath();
123
+ schema(path);
124
+ // Получаем валидаторы БЕЗ сохранения в реестр
125
+ const context = tempRegistry.getCurrentContext();
126
+ tempValidators = context?.getValidators() || [];
127
+ // Отменяем регистрацию (не сохраняем в реестр формы)
128
+ tempRegistry.cancelRegistration();
129
+ cancelled = true;
130
+ // Очищаем текущие ошибки полей
131
+ form.clearErrors();
132
+ // Валидируем все FieldNode (field-level валидация)
133
+ // ВАЖНО: Используем collectAllFieldNodes вместо getAllFields,
134
+ // чтобы избежать вызова validate() на вложенных GroupNode/ArrayNode,
135
+ // которые триггерят свой validationRegistry с полной схемой валидации
136
+ const allFieldNodes = collectAllFieldNodes(form);
137
+ await Promise.all(allFieldNodes.map((field) => field.validate()));
138
+ // Применяем contextual validators
139
+ if (tempValidators.length > 0) {
140
+ await form.applyContextualValidators(tempValidators);
141
+ }
142
+ // Проверяем результат
143
+ return form.valid.value;
144
+ }
145
+ catch (error) {
146
+ // В случае ошибки отменяем регистрацию только если еще не отменили
147
+ if (!cancelled) {
148
+ tempRegistry.cancelRegistration();
149
+ }
150
+ throw error;
151
+ }
152
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Применение валидаторов к форме
3
+ *
4
+ * Извлечено из GroupNode для соблюдения SRP (Single Responsibility Principle).
5
+ * Отвечает только за логику применения валидаторов к полям формы.
6
+ *
7
+ * @template T Тип формы
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * class GroupNode {
12
+ * private readonly validationApplicator = new ValidationApplicator(this);
13
+ *
14
+ * async applyContextualValidators(validators: ValidatorRegistration[]) {
15
+ * await this.validationApplicator.apply(validators);
16
+ * }
17
+ * }
18
+ * ```
19
+ */
20
+ import type { GroupNode } from '../nodes/group-node';
21
+ import type { ValidatorRegistration } from '../types';
22
+ /**
23
+ * Класс для применения валидаторов к форме
24
+ *
25
+ * Выполняет:
26
+ * - Группировку валидаторов по полям
27
+ * - Фильтрацию по условиям (condition)
28
+ * - Применение sync/async валидаторов к FieldNode
29
+ * - Применение tree валидаторов (кросс-полевая валидация)
30
+ *
31
+ * @template T Тип формы (объект)
32
+ */
33
+ export declare class ValidationApplicator<T> {
34
+ private readonly form;
35
+ constructor(form: GroupNode<T>);
36
+ /**
37
+ * Применить валидаторы к полям формы
38
+ *
39
+ * Этапы применения:
40
+ * 1. Разделение валидаторов на field и tree
41
+ * 2. Применение field валидаторов (sync/async)
42
+ * 3. Применение tree валидаторов (кросс-полевая валидация)
43
+ *
44
+ * @param validators Зарегистрированные валидаторы
45
+ */
46
+ apply(validators: ValidatorRegistration[]): Promise<void>;
47
+ /**
48
+ * Группировка валидаторов по типам
49
+ *
50
+ * Разделяет валидаторы на:
51
+ * - Field validators (sync/async) - группируются по fieldPath
52
+ * - Tree validators - применяются ко всей форме
53
+ *
54
+ * @param validators Все зарегистрированные валидаторы
55
+ * @returns Сгруппированные валидаторы
56
+ */
57
+ private groupValidators;
58
+ /**
59
+ * Применение field валидаторов к полям
60
+ *
61
+ * Для каждого поля:
62
+ * 1. Найти FieldNode по пути
63
+ * 2. Проверить условия (condition)
64
+ * 3. Выполнить sync/async валидаторы
65
+ * 4. Установить ошибки на поле
66
+ *
67
+ * @param validatorsByField Валидаторы, сгруппированные по полям
68
+ */
69
+ private applyFieldValidators;
70
+ /**
71
+ * Применение tree валидаторов (кросс-полевая валидация)
72
+ *
73
+ * Tree валидаторы имеют доступ ко всей форме через TreeValidationContext.
74
+ * Ошибки устанавливаются на targetField (если указано).
75
+ *
76
+ * @param treeValidators Список tree валидаторов
77
+ */
78
+ private applyTreeValidators;
79
+ /**
80
+ * Проверка условия (condition) для валидатора
81
+ *
82
+ * Условие определяет, должен ли валидатор выполняться.
83
+ * Использует getFieldByPath для поддержки вложенных путей.
84
+ *
85
+ * @param condition Условие валидатора
86
+ * @returns true, если условие выполнено
87
+ */
88
+ private checkCondition;
89
+ }