@reformer/core 1.1.0 → 2.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 (99) hide show
  1. package/dist/behaviors-DzYL8kY_.js +499 -0
  2. package/dist/behaviors.d.ts +6 -2
  3. package/dist/behaviors.js +19 -227
  4. package/dist/core/behavior/behavior-context.d.ts +6 -2
  5. package/dist/core/behavior/create-field-path.d.ts +3 -16
  6. package/dist/core/nodes/group-node.d.ts +14 -193
  7. package/dist/core/types/form-context.d.ts +10 -4
  8. package/dist/core/utils/field-path.d.ts +48 -0
  9. package/dist/core/utils/index.d.ts +1 -0
  10. package/dist/core/validation/core/validate-tree.d.ts +10 -4
  11. package/dist/core/validation/field-path.d.ts +3 -39
  12. package/dist/core/validation/validation-context.d.ts +23 -0
  13. package/dist/hooks/types.d.ts +328 -0
  14. package/dist/hooks/useFormControl.d.ts +13 -37
  15. package/dist/hooks/useFormControlValue.d.ts +167 -0
  16. package/dist/hooks/useSignalSubscription.d.ts +17 -0
  17. package/dist/index.d.ts +6 -1
  18. package/dist/index.js +2886 -8
  19. package/dist/{create-field-path-CdPF3lIK.js → registry-helpers-BRxAr6nG.js} +133 -347
  20. package/dist/validators-gXoHPdqM.js +418 -0
  21. package/dist/validators.d.ts +6 -2
  22. package/dist/validators.js +29 -296
  23. package/llms.txt +1283 -22
  24. package/package.json +8 -4
  25. package/dist/core/behavior/behavior-applicator.d.ts +0 -71
  26. package/dist/core/behavior/behavior-applicator.js +0 -92
  27. package/dist/core/behavior/behavior-context.js +0 -38
  28. package/dist/core/behavior/behavior-registry.js +0 -198
  29. package/dist/core/behavior/behaviors/compute-from.js +0 -84
  30. package/dist/core/behavior/behaviors/copy-from.js +0 -64
  31. package/dist/core/behavior/behaviors/enable-when.js +0 -81
  32. package/dist/core/behavior/behaviors/index.js +0 -11
  33. package/dist/core/behavior/behaviors/reset-when.js +0 -63
  34. package/dist/core/behavior/behaviors/revalidate-when.js +0 -51
  35. package/dist/core/behavior/behaviors/sync-fields.js +0 -66
  36. package/dist/core/behavior/behaviors/transform-value.js +0 -110
  37. package/dist/core/behavior/behaviors/watch-field.js +0 -56
  38. package/dist/core/behavior/compose-behavior.js +0 -166
  39. package/dist/core/behavior/create-field-path.js +0 -69
  40. package/dist/core/behavior/index.js +0 -17
  41. package/dist/core/behavior/types.js +0 -7
  42. package/dist/core/context/form-context-impl.js +0 -37
  43. package/dist/core/factories/index.js +0 -6
  44. package/dist/core/factories/node-factory.js +0 -281
  45. package/dist/core/nodes/array-node.js +0 -534
  46. package/dist/core/nodes/field-node.js +0 -510
  47. package/dist/core/nodes/form-node.js +0 -343
  48. package/dist/core/nodes/group-node/field-registry.d.ts +0 -191
  49. package/dist/core/nodes/group-node/field-registry.js +0 -215
  50. package/dist/core/nodes/group-node/index.d.ts +0 -11
  51. package/dist/core/nodes/group-node/index.js +0 -11
  52. package/dist/core/nodes/group-node/proxy-builder.d.ts +0 -71
  53. package/dist/core/nodes/group-node/proxy-builder.js +0 -161
  54. package/dist/core/nodes/group-node/state-manager.d.ts +0 -184
  55. package/dist/core/nodes/group-node/state-manager.js +0 -265
  56. package/dist/core/nodes/group-node.js +0 -770
  57. package/dist/core/types/deep-schema.js +0 -11
  58. package/dist/core/types/field-path.js +0 -4
  59. package/dist/core/types/form-context.js +0 -25
  60. package/dist/core/types/group-node-proxy.js +0 -31
  61. package/dist/core/types/index.js +0 -4
  62. package/dist/core/types/validation-schema.js +0 -10
  63. package/dist/core/utils/create-form.js +0 -24
  64. package/dist/core/utils/debounce.js +0 -197
  65. package/dist/core/utils/error-handler.js +0 -226
  66. package/dist/core/utils/field-path-navigator.js +0 -374
  67. package/dist/core/utils/index.js +0 -14
  68. package/dist/core/utils/registry-helpers.js +0 -79
  69. package/dist/core/utils/registry-stack.js +0 -86
  70. package/dist/core/utils/resources.js +0 -69
  71. package/dist/core/utils/subscription-manager.js +0 -214
  72. package/dist/core/utils/type-guards.js +0 -169
  73. package/dist/core/validation/core/apply-when.js +0 -41
  74. package/dist/core/validation/core/apply.js +0 -38
  75. package/dist/core/validation/core/index.js +0 -8
  76. package/dist/core/validation/core/validate-async.js +0 -45
  77. package/dist/core/validation/core/validate-tree.js +0 -37
  78. package/dist/core/validation/core/validate.js +0 -38
  79. package/dist/core/validation/field-path.js +0 -147
  80. package/dist/core/validation/index.js +0 -33
  81. package/dist/core/validation/validate-form.js +0 -152
  82. package/dist/core/validation/validation-applicator.js +0 -217
  83. package/dist/core/validation/validation-context.js +0 -75
  84. package/dist/core/validation/validation-registry.js +0 -298
  85. package/dist/core/validation/validators/array-validators.js +0 -86
  86. package/dist/core/validation/validators/date.js +0 -117
  87. package/dist/core/validation/validators/email.js +0 -60
  88. package/dist/core/validation/validators/index.js +0 -14
  89. package/dist/core/validation/validators/max-length.js +0 -60
  90. package/dist/core/validation/validators/max.js +0 -60
  91. package/dist/core/validation/validators/min-length.js +0 -60
  92. package/dist/core/validation/validators/min.js +0 -60
  93. package/dist/core/validation/validators/number.js +0 -90
  94. package/dist/core/validation/validators/pattern.js +0 -62
  95. package/dist/core/validation/validators/phone.js +0 -58
  96. package/dist/core/validation/validators/required.js +0 -69
  97. package/dist/core/validation/validators/url.js +0 -55
  98. package/dist/hooks/useFormControl.js +0 -298
  99. package/dist/node-factory-D7DOnSSN.js +0 -3200
@@ -1,147 +0,0 @@
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
- }
@@ -1,33 +0,0 @@
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';
@@ -1,152 +0,0 @@
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
- }
@@ -1,217 +0,0 @@
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 { ValidationContextImpl, TreeValidationContextImpl } from './validation-context';
21
- import { isFieldNode } from '../utils/type-guards';
22
- import { FormErrorHandler, ErrorStrategy } from '../utils/error-handler';
23
- /**
24
- * Класс для применения валидаторов к форме
25
- *
26
- * Выполняет:
27
- * - Группировку валидаторов по полям
28
- * - Фильтрацию по условиям (condition)
29
- * - Применение sync/async валидаторов к FieldNode
30
- * - Применение tree валидаторов (кросс-полевая валидация)
31
- *
32
- * @template T Тип формы (объект)
33
- */
34
- export class ValidationApplicator {
35
- form;
36
- constructor(form) {
37
- this.form = form;
38
- }
39
- /**
40
- * Применить валидаторы к полям формы
41
- *
42
- * Этапы применения:
43
- * 1. Разделение валидаторов на field и tree
44
- * 2. Применение field валидаторов (sync/async)
45
- * 3. Применение tree валидаторов (кросс-полевая валидация)
46
- *
47
- * @param validators Зарегистрированные валидаторы
48
- */
49
- async apply(validators) {
50
- // 1. Группировка валидаторов
51
- const { validatorsByField, treeValidators } = this.groupValidators(validators);
52
- // 2. Применение field валидаторов
53
- await this.applyFieldValidators(validatorsByField);
54
- // 3. Применение tree валидаторов
55
- this.applyTreeValidators(treeValidators);
56
- }
57
- /**
58
- * Группировка валидаторов по типам
59
- *
60
- * Разделяет валидаторы на:
61
- * - Field validators (sync/async) - группируются по fieldPath
62
- * - Tree validators - применяются ко всей форме
63
- *
64
- * @param validators Все зарегистрированные валидаторы
65
- * @returns Сгруппированные валидаторы
66
- */
67
- groupValidators(validators) {
68
- const validatorsByField = new Map();
69
- const treeValidators = [];
70
- for (const registration of validators) {
71
- if (registration.type === 'tree') {
72
- treeValidators.push(registration);
73
- }
74
- else {
75
- const existing = validatorsByField.get(registration.fieldPath) || [];
76
- existing.push(registration);
77
- validatorsByField.set(registration.fieldPath, existing);
78
- }
79
- }
80
- return { validatorsByField, treeValidators };
81
- }
82
- /**
83
- * Применение field валидаторов к полям
84
- *
85
- * Для каждого поля:
86
- * 1. Найти FieldNode по пути
87
- * 2. Проверить условия (condition)
88
- * 3. Выполнить sync/async валидаторы
89
- * 4. Установить ошибки на поле
90
- *
91
- * @param validatorsByField Валидаторы, сгруппированные по полям
92
- */
93
- async applyFieldValidators(validatorsByField) {
94
- for (const [fieldPath, fieldValidators] of validatorsByField) {
95
- // Поддержка вложенных путей (например, "personalData.lastName")
96
- const control = this.form.getFieldByPath(fieldPath);
97
- if (!control) {
98
- if (import.meta.env.DEV) {
99
- throw new Error(`Field "${fieldPath}" not found in GroupNode`);
100
- }
101
- console.warn(`Field ${fieldPath} not found in GroupNode`);
102
- continue;
103
- }
104
- // Валидация работает только с FieldNode
105
- if (!isFieldNode(control)) {
106
- if (process.env.NODE_ENV !== 'production') {
107
- console.warn(`Validation can only run on FieldNode, skipping ${fieldPath}`);
108
- }
109
- continue;
110
- }
111
- const errors = [];
112
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
113
- const context = new ValidationContextImpl(this.form, fieldPath, control);
114
- // Выполнение валидаторов с учетом условий
115
- for (const registration of fieldValidators) {
116
- // Проверка условия (condition)
117
- if (registration.condition) {
118
- const shouldApply = this.checkCondition(registration.condition);
119
- if (!shouldApply) {
120
- continue;
121
- }
122
- }
123
- // Выполнение валидатора
124
- // Новый паттерн: (value, ctx) => ValidationError | null
125
- try {
126
- let error = null;
127
- const value = context.value();
128
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
129
- const validator = registration.validator;
130
- if (registration.type === 'sync') {
131
- error = validator(value, context);
132
- }
133
- else if (registration.type === 'async') {
134
- error = await validator(value, context);
135
- }
136
- if (error) {
137
- errors.push(error);
138
- }
139
- }
140
- catch (e) {
141
- FormErrorHandler.handle(e, `ValidationApplicator: validator for ${fieldPath}`, ErrorStrategy.LOG);
142
- }
143
- }
144
- // Установка ошибок на поле
145
- if (errors.length > 0) {
146
- control.setErrors(errors);
147
- }
148
- else {
149
- // Очистить ошибки, если они были contextual
150
- if (control.errors.value.length > 0 &&
151
- !control.errors.value.some((e) => e.code !== 'contextual')) {
152
- control.clearErrors();
153
- }
154
- }
155
- }
156
- }
157
- /**
158
- * Применение tree валидаторов (кросс-полевая валидация)
159
- *
160
- * Tree валидаторы имеют доступ ко всей форме через TreeValidationContext.
161
- * Ошибки устанавливаются на targetField (если указано).
162
- *
163
- * @param treeValidators Список tree валидаторов
164
- */
165
- applyTreeValidators(treeValidators) {
166
- for (const registration of treeValidators) {
167
- const context = new TreeValidationContextImpl(this.form);
168
- // Проверка условия (condition)
169
- if (registration.condition) {
170
- const shouldApply = this.checkCondition(registration.condition);
171
- if (!shouldApply) {
172
- continue;
173
- }
174
- }
175
- // Выполнение tree валидатора
176
- try {
177
- // Tree валидаторы должны использовать TreeValidatorFn
178
- if (registration.type !== 'tree') {
179
- continue;
180
- }
181
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
182
- const error = registration.validator(context);
183
- if (error && registration.options && 'targetField' in registration.options) {
184
- const targetField = registration.options.targetField;
185
- if (targetField) {
186
- const targetControl = this.form.getFieldByPath(String(targetField));
187
- if (targetControl && isFieldNode(targetControl)) {
188
- const existingErrors = targetControl.errors.value;
189
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
190
- targetControl.setErrors([...existingErrors, error]);
191
- }
192
- }
193
- }
194
- }
195
- catch (e) {
196
- FormErrorHandler.handle(e, 'ValidationApplicator: tree validator', ErrorStrategy.LOG);
197
- }
198
- }
199
- }
200
- /**
201
- * Проверка условия (condition) для валидатора
202
- *
203
- * Условие определяет, должен ли валидатор выполняться.
204
- * Использует getFieldByPath для поддержки вложенных путей.
205
- *
206
- * @param condition Условие валидатора
207
- * @returns true, если условие выполнено
208
- */
209
- checkCondition(condition) {
210
- const conditionField = this.form.getFieldByPath(condition.fieldPath);
211
- if (!conditionField) {
212
- return false;
213
- }
214
- const conditionValue = conditionField.value.value;
215
- return condition.conditionFn(conditionValue);
216
- }
217
- }
@@ -1,75 +0,0 @@
1
- /**
2
- * Реализация контекста валидации
3
- */
4
- import { isFormNode } from '../utils/type-guards';
5
- // ============================================================================
6
- // Field Validation Context (для обычных валидаторов)
7
- // ============================================================================
8
- /**
9
- * Реализация контекста валидации для отдельного поля
10
- * Реализует FormContext
11
- */
12
- export class ValidationContextImpl {
13
- _form;
14
- control;
15
- /**
16
- * Форма с типизированным Proxy-доступом к полям
17
- */
18
- form;
19
- constructor(form, _fieldKey, control) {
20
- this._form = form;
21
- this.control = control;
22
- // Получаем Proxy для типизированного доступа
23
- this.form = (form
24
- ._proxyInstance || form.getProxy());
25
- }
26
- /**
27
- * Получить текущее значение поля (внутренний метод для validation-applicator)
28
- * @internal
29
- */
30
- value() {
31
- return this.control.value.value;
32
- }
33
- /**
34
- * Безопасно установить значение поля по строковому пути
35
- * Автоматически использует emitEvent: false для предотвращения циклов
36
- */
37
- setFieldValue(path, value) {
38
- const node = this._form.getFieldByPath(path);
39
- if (node && isFormNode(node)) {
40
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
41
- node.setValue(value, { emitEvent: false });
42
- }
43
- }
44
- }
45
- // ============================================================================
46
- // Tree Validation Context (для cross-field валидаторов)
47
- // ============================================================================
48
- /**
49
- * Реализация контекста для cross-field валидации
50
- * Реализует FormContext
51
- */
52
- export class TreeValidationContextImpl {
53
- _form;
54
- /**
55
- * Форма с типизированным Proxy-доступом к полям
56
- */
57
- form;
58
- constructor(form) {
59
- this._form = form;
60
- // Получаем Proxy для типизированного доступа
61
- this.form = (form
62
- ._proxyInstance || form.getProxy());
63
- }
64
- /**
65
- * Безопасно установить значение поля по строковому пути
66
- * Автоматически использует emitEvent: false для предотвращения циклов
67
- */
68
- setFieldValue(path, value) {
69
- const node = this._form.getFieldByPath(path);
70
- if (node && isFormNode(node)) {
71
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
72
- node.setValue(value, { emitEvent: false });
73
- }
74
- }
75
- }