@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.
- package/LICENSE +21 -0
- package/README.md +53 -0
- package/dist/behaviors.d.ts +2 -0
- package/dist/behaviors.js +230 -0
- package/dist/core/behavior/behavior-applicator.d.ts +71 -0
- package/dist/core/behavior/behavior-applicator.js +92 -0
- package/dist/core/behavior/behavior-context.d.ts +29 -0
- package/dist/core/behavior/behavior-context.js +38 -0
- package/dist/core/behavior/behavior-registry.d.ts +97 -0
- package/dist/core/behavior/behavior-registry.js +198 -0
- package/dist/core/behavior/behaviors/compute-from.d.ts +41 -0
- package/dist/core/behavior/behaviors/compute-from.js +84 -0
- package/dist/core/behavior/behaviors/copy-from.d.ts +31 -0
- package/dist/core/behavior/behaviors/copy-from.js +64 -0
- package/dist/core/behavior/behaviors/enable-when.d.ts +49 -0
- package/dist/core/behavior/behaviors/enable-when.js +81 -0
- package/dist/core/behavior/behaviors/index.d.ts +11 -0
- package/dist/core/behavior/behaviors/index.js +11 -0
- package/dist/core/behavior/behaviors/reset-when.d.ts +51 -0
- package/dist/core/behavior/behaviors/reset-when.js +63 -0
- package/dist/core/behavior/behaviors/revalidate-when.d.ts +30 -0
- package/dist/core/behavior/behaviors/revalidate-when.js +51 -0
- package/dist/core/behavior/behaviors/sync-fields.d.ts +28 -0
- package/dist/core/behavior/behaviors/sync-fields.js +66 -0
- package/dist/core/behavior/behaviors/transform-value.d.ts +120 -0
- package/dist/core/behavior/behaviors/transform-value.js +110 -0
- package/dist/core/behavior/behaviors/watch-field.d.ts +35 -0
- package/dist/core/behavior/behaviors/watch-field.js +56 -0
- package/dist/core/behavior/compose-behavior.d.ts +106 -0
- package/dist/core/behavior/compose-behavior.js +166 -0
- package/dist/core/behavior/create-field-path.d.ts +20 -0
- package/dist/core/behavior/create-field-path.js +69 -0
- package/dist/core/behavior/index.d.ts +12 -0
- package/dist/core/behavior/index.js +17 -0
- package/dist/core/behavior/types.d.ts +152 -0
- package/dist/core/behavior/types.js +7 -0
- package/dist/core/context/form-context-impl.d.ts +29 -0
- package/dist/core/context/form-context-impl.js +37 -0
- package/dist/core/factories/index.d.ts +6 -0
- package/dist/core/factories/index.js +6 -0
- package/dist/core/factories/node-factory.d.ts +209 -0
- package/dist/core/factories/node-factory.js +281 -0
- package/dist/core/nodes/array-node.d.ts +308 -0
- package/dist/core/nodes/array-node.js +534 -0
- package/dist/core/nodes/field-node.d.ts +269 -0
- package/dist/core/nodes/field-node.js +510 -0
- package/dist/core/nodes/form-node.d.ts +342 -0
- package/dist/core/nodes/form-node.js +343 -0
- package/dist/core/nodes/group-node/field-registry.d.ts +191 -0
- package/dist/core/nodes/group-node/field-registry.js +215 -0
- package/dist/core/nodes/group-node/index.d.ts +11 -0
- package/dist/core/nodes/group-node/index.js +11 -0
- package/dist/core/nodes/group-node/proxy-builder.d.ts +71 -0
- package/dist/core/nodes/group-node/proxy-builder.js +161 -0
- package/dist/core/nodes/group-node/state-manager.d.ts +184 -0
- package/dist/core/nodes/group-node/state-manager.js +265 -0
- package/dist/core/nodes/group-node.d.ts +494 -0
- package/dist/core/nodes/group-node.js +770 -0
- package/dist/core/types/deep-schema.d.ts +78 -0
- package/dist/core/types/deep-schema.js +11 -0
- package/dist/core/types/field-path.d.ts +42 -0
- package/dist/core/types/field-path.js +4 -0
- package/dist/core/types/form-context.d.ts +83 -0
- package/dist/core/types/form-context.js +25 -0
- package/dist/core/types/group-node-proxy.d.ts +135 -0
- package/dist/core/types/group-node-proxy.js +31 -0
- package/dist/core/types/index.d.ts +163 -0
- package/dist/core/types/index.js +4 -0
- package/dist/core/types/validation-schema.d.ts +104 -0
- package/dist/core/types/validation-schema.js +10 -0
- package/dist/core/utils/create-form.d.ts +61 -0
- package/dist/core/utils/create-form.js +24 -0
- package/dist/core/utils/debounce.d.ts +160 -0
- package/dist/core/utils/debounce.js +197 -0
- package/dist/core/utils/error-handler.d.ts +180 -0
- package/dist/core/utils/error-handler.js +226 -0
- package/dist/core/utils/field-path-navigator.d.ts +240 -0
- package/dist/core/utils/field-path-navigator.js +374 -0
- package/dist/core/utils/index.d.ts +14 -0
- package/dist/core/utils/index.js +14 -0
- package/dist/core/utils/registry-helpers.d.ts +50 -0
- package/dist/core/utils/registry-helpers.js +79 -0
- package/dist/core/utils/registry-stack.d.ts +69 -0
- package/dist/core/utils/registry-stack.js +86 -0
- package/dist/core/utils/resources.d.ts +41 -0
- package/dist/core/utils/resources.js +69 -0
- package/dist/core/utils/subscription-manager.d.ts +180 -0
- package/dist/core/utils/subscription-manager.js +214 -0
- package/dist/core/utils/type-guards.d.ts +116 -0
- package/dist/core/utils/type-guards.js +169 -0
- package/dist/core/validation/core/apply-when.d.ts +28 -0
- package/dist/core/validation/core/apply-when.js +41 -0
- package/dist/core/validation/core/apply.d.ts +63 -0
- package/dist/core/validation/core/apply.js +38 -0
- package/dist/core/validation/core/index.d.ts +8 -0
- package/dist/core/validation/core/index.js +8 -0
- package/dist/core/validation/core/validate-async.d.ts +42 -0
- package/dist/core/validation/core/validate-async.js +45 -0
- package/dist/core/validation/core/validate-tree.d.ts +35 -0
- package/dist/core/validation/core/validate-tree.js +37 -0
- package/dist/core/validation/core/validate.d.ts +32 -0
- package/dist/core/validation/core/validate.js +38 -0
- package/dist/core/validation/field-path.d.ts +43 -0
- package/dist/core/validation/field-path.js +147 -0
- package/dist/core/validation/index.d.ts +21 -0
- package/dist/core/validation/index.js +33 -0
- package/dist/core/validation/validate-form.d.ts +85 -0
- package/dist/core/validation/validate-form.js +152 -0
- package/dist/core/validation/validation-applicator.d.ts +89 -0
- package/dist/core/validation/validation-applicator.js +217 -0
- package/dist/core/validation/validation-context.d.ts +47 -0
- package/dist/core/validation/validation-context.js +75 -0
- package/dist/core/validation/validation-registry.d.ts +156 -0
- package/dist/core/validation/validation-registry.js +298 -0
- package/dist/core/validation/validators/array-validators.d.ts +63 -0
- package/dist/core/validation/validators/array-validators.js +86 -0
- package/dist/core/validation/validators/date.d.ts +38 -0
- package/dist/core/validation/validators/date.js +117 -0
- package/dist/core/validation/validators/email.d.ts +44 -0
- package/dist/core/validation/validators/email.js +60 -0
- package/dist/core/validation/validators/index.d.ts +14 -0
- package/dist/core/validation/validators/index.js +14 -0
- package/dist/core/validation/validators/max-length.d.ts +45 -0
- package/dist/core/validation/validators/max-length.js +60 -0
- package/dist/core/validation/validators/max.d.ts +45 -0
- package/dist/core/validation/validators/max.js +60 -0
- package/dist/core/validation/validators/min-length.d.ts +45 -0
- package/dist/core/validation/validators/min-length.js +60 -0
- package/dist/core/validation/validators/min.d.ts +45 -0
- package/dist/core/validation/validators/min.js +60 -0
- package/dist/core/validation/validators/number.d.ts +38 -0
- package/dist/core/validation/validators/number.js +90 -0
- package/dist/core/validation/validators/pattern.d.ts +47 -0
- package/dist/core/validation/validators/pattern.js +62 -0
- package/dist/core/validation/validators/phone.d.ts +34 -0
- package/dist/core/validation/validators/phone.js +58 -0
- package/dist/core/validation/validators/required.d.ts +48 -0
- package/dist/core/validation/validators/required.js +69 -0
- package/dist/core/validation/validators/url.d.ts +29 -0
- package/dist/core/validation/validators/url.js +55 -0
- package/dist/create-field-path-CdPF3lIK.js +704 -0
- package/dist/hooks/useFormControl.d.ts +48 -0
- package/dist/hooks/useFormControl.js +298 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +8 -0
- package/dist/node-factory-D7DOnSSN.js +3200 -0
- package/dist/validators.d.ts +2 -0
- package/dist/validators.js +298 -0
- package/llms.txt +847 -0
- package/package.json +86 -0
|
@@ -0,0 +1,217 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Реализация контекста валидации
|
|
3
|
+
*/
|
|
4
|
+
import type { GroupNode } from '../nodes/group-node';
|
|
5
|
+
import type { FieldNode } from '../nodes/field-node';
|
|
6
|
+
import type { GroupNodeWithControls } from '../types/group-node-proxy';
|
|
7
|
+
import type { FormContext } from '../types/form-context';
|
|
8
|
+
/**
|
|
9
|
+
* Реализация контекста валидации для отдельного поля
|
|
10
|
+
* Реализует FormContext
|
|
11
|
+
*/
|
|
12
|
+
export declare class ValidationContextImpl<TForm, TField> implements FormContext<TForm> {
|
|
13
|
+
private _form;
|
|
14
|
+
private control;
|
|
15
|
+
/**
|
|
16
|
+
* Форма с типизированным Proxy-доступом к полям
|
|
17
|
+
*/
|
|
18
|
+
readonly form: GroupNodeWithControls<TForm>;
|
|
19
|
+
constructor(form: GroupNode<TForm>, _fieldKey: keyof TForm, control: FieldNode<TField>);
|
|
20
|
+
/**
|
|
21
|
+
* Получить текущее значение поля (внутренний метод для validation-applicator)
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
value(): TField;
|
|
25
|
+
/**
|
|
26
|
+
* Безопасно установить значение поля по строковому пути
|
|
27
|
+
* Автоматически использует emitEvent: false для предотвращения циклов
|
|
28
|
+
*/
|
|
29
|
+
setFieldValue(path: string, value: unknown): void;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Реализация контекста для cross-field валидации
|
|
33
|
+
* Реализует FormContext
|
|
34
|
+
*/
|
|
35
|
+
export declare class TreeValidationContextImpl<TForm> implements FormContext<TForm> {
|
|
36
|
+
private _form;
|
|
37
|
+
/**
|
|
38
|
+
* Форма с типизированным Proxy-доступом к полям
|
|
39
|
+
*/
|
|
40
|
+
readonly form: GroupNodeWithControls<TForm>;
|
|
41
|
+
constructor(form: GroupNode<TForm>);
|
|
42
|
+
/**
|
|
43
|
+
* Безопасно установить значение поля по строковому пути
|
|
44
|
+
* Автоматически использует emitEvent: false для предотвращения циклов
|
|
45
|
+
*/
|
|
46
|
+
setFieldValue(path: string, value: unknown): void;
|
|
47
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ValidationRegistry - система регистрации и применения валидаторов
|
|
3
|
+
*
|
|
4
|
+
* Работает как стек контекстов:
|
|
5
|
+
* 1. При вызове validation schema функции создается новый контекст
|
|
6
|
+
* 2. Все вызовы validate(), applyWhen() и т.д. регистрируют валидаторы в текущем контексте
|
|
7
|
+
* 3. После завершения схемы валидаторы применяются к GroupNode
|
|
8
|
+
*/
|
|
9
|
+
import type { GroupNode } from '../nodes/group-node';
|
|
10
|
+
import { FormFields } from '../types';
|
|
11
|
+
import type { ValidatorRegistration, ContextualValidatorFn, ContextualAsyncValidatorFn, TreeValidatorFn, ConditionFn, ValidateOptions, ValidateAsyncOptions, ValidateTreeOptions } from '../types/validation-schema';
|
|
12
|
+
/**
|
|
13
|
+
* Контекст регистрации валидаторов
|
|
14
|
+
*/
|
|
15
|
+
declare class RegistrationContext {
|
|
16
|
+
private validators;
|
|
17
|
+
private conditionStack;
|
|
18
|
+
/**
|
|
19
|
+
* Добавить валидатор в контекст
|
|
20
|
+
*/
|
|
21
|
+
addValidator(registration: ValidatorRegistration): void;
|
|
22
|
+
/**
|
|
23
|
+
* Войти в условный блок
|
|
24
|
+
*/
|
|
25
|
+
enterCondition(fieldPath: string, conditionFn: ConditionFn<unknown>): void;
|
|
26
|
+
/**
|
|
27
|
+
* Выйти из условного блока
|
|
28
|
+
*/
|
|
29
|
+
exitCondition(): void;
|
|
30
|
+
/**
|
|
31
|
+
* Получить все зарегистрированные валидаторы
|
|
32
|
+
*/
|
|
33
|
+
getValidators(): ValidatorRegistration[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Реестр валидаторов для формы
|
|
37
|
+
*
|
|
38
|
+
* Каждый экземпляр GroupNode создает собственный реестр (композиция).
|
|
39
|
+
* Устраняет race conditions и изолирует формы друг от друга.
|
|
40
|
+
*
|
|
41
|
+
* Context stack используется для tracking текущего активного реестра:
|
|
42
|
+
* - beginRegistration() помещает this в global stack
|
|
43
|
+
* - endRegistration() извлекает из global stack
|
|
44
|
+
* - getCurrent() возвращает текущий активный реестр
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* class GroupNode {
|
|
49
|
+
* private readonly validationRegistry = new ValidationRegistry();
|
|
50
|
+
*
|
|
51
|
+
* applyValidationSchema(schemaFn) {
|
|
52
|
+
* this.validationRegistry.beginRegistration(); // Pushes this to global stack
|
|
53
|
+
* schemaFn(createFieldPath(this)); // Uses getCurrent()
|
|
54
|
+
* this.validationRegistry.endRegistration(this); // Pops from global stack
|
|
55
|
+
* }
|
|
56
|
+
* }
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export declare class ValidationRegistry {
|
|
60
|
+
/**
|
|
61
|
+
* Global stack активных реестров
|
|
62
|
+
* Используется для изоляции форм друг от друга
|
|
63
|
+
*/
|
|
64
|
+
private static registryStack;
|
|
65
|
+
private contextStack;
|
|
66
|
+
private validators;
|
|
67
|
+
/**
|
|
68
|
+
* Получить текущий активный реестр из global stack
|
|
69
|
+
*
|
|
70
|
+
* @returns Текущий активный реестр или null
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* // В schema-validators.ts
|
|
75
|
+
* export function required(...) {
|
|
76
|
+
* const registry = ValidationRegistry.getCurrent();
|
|
77
|
+
* if (registry) {
|
|
78
|
+
* registry.registerSync(...);
|
|
79
|
+
* }
|
|
80
|
+
* }
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
static getCurrent(): ValidationRegistry | null;
|
|
84
|
+
/**
|
|
85
|
+
* Начать регистрацию валидаторов для формы
|
|
86
|
+
*
|
|
87
|
+
* Помещает this в global stack для изоляции форм
|
|
88
|
+
*/
|
|
89
|
+
beginRegistration(): RegistrationContext;
|
|
90
|
+
/**
|
|
91
|
+
* Завершить регистрацию и применить валидаторы к GroupNode
|
|
92
|
+
*
|
|
93
|
+
* Извлекает this из global stack
|
|
94
|
+
*
|
|
95
|
+
* Сохраняет валидаторы в локальном состоянии (this.validators) вместо глобального WeakMap.
|
|
96
|
+
*/
|
|
97
|
+
endRegistration<T extends FormFields>(form: GroupNode<T>): void;
|
|
98
|
+
/**
|
|
99
|
+
* Отменить регистрацию без применения валидаторов
|
|
100
|
+
* Используется для временной валидации (например, в validateForm)
|
|
101
|
+
*
|
|
102
|
+
* Извлекает this из global stack
|
|
103
|
+
*/
|
|
104
|
+
cancelRegistration(): void;
|
|
105
|
+
/**
|
|
106
|
+
* Получить текущий контекст регистрации
|
|
107
|
+
*/
|
|
108
|
+
getCurrentContext(): RegistrationContext | undefined;
|
|
109
|
+
/**
|
|
110
|
+
* Зарегистрировать синхронный валидатор
|
|
111
|
+
*/
|
|
112
|
+
registerSync<TForm = unknown, TField = unknown>(fieldPath: string, validator: ContextualValidatorFn<TForm, TField>, options?: ValidateOptions): void;
|
|
113
|
+
/**
|
|
114
|
+
* Зарегистрировать асинхронный валидатор
|
|
115
|
+
*/
|
|
116
|
+
registerAsync<TForm = unknown, TField = unknown>(fieldPath: string, validator: ContextualAsyncValidatorFn<TForm, TField>, options?: ValidateAsyncOptions): void;
|
|
117
|
+
/**
|
|
118
|
+
* Зарегистрировать tree валидатор
|
|
119
|
+
*/
|
|
120
|
+
registerTree<TForm = unknown>(validator: TreeValidatorFn<TForm>, options?: ValidateTreeOptions): void;
|
|
121
|
+
/**
|
|
122
|
+
* Войти в условный блок
|
|
123
|
+
*/
|
|
124
|
+
enterCondition(fieldPath: string, conditionFn: ConditionFn<unknown>): void;
|
|
125
|
+
/**
|
|
126
|
+
* Выйти из условного блока
|
|
127
|
+
*/
|
|
128
|
+
exitCondition(): void;
|
|
129
|
+
/**
|
|
130
|
+
* Зарегистрировать validation schema для элементов массива
|
|
131
|
+
*
|
|
132
|
+
* Используется функцией validateItems() для регистрации схемы валидации,
|
|
133
|
+
* которая будет применяться к каждому элементу ArrayNode.
|
|
134
|
+
*
|
|
135
|
+
* @param fieldPath - Путь к ArrayNode полю
|
|
136
|
+
* @param itemSchemaFn - Validation schema для элемента массива
|
|
137
|
+
*/
|
|
138
|
+
registerArrayItemValidation(fieldPath: string, itemSchemaFn: unknown): void;
|
|
139
|
+
/**
|
|
140
|
+
* Получить зарегистрированные валидаторы для этого реестра
|
|
141
|
+
*
|
|
142
|
+
* Возвращает локальный массив валидаторов (без аргумента form).
|
|
143
|
+
*/
|
|
144
|
+
getValidators(): ValidatorRegistration[];
|
|
145
|
+
/**
|
|
146
|
+
* Применить зарегистрированные валидаторы к GroupNode
|
|
147
|
+
* @private
|
|
148
|
+
*/
|
|
149
|
+
private applyValidators;
|
|
150
|
+
/**
|
|
151
|
+
* Применить array-items validators к ArrayNode элементам
|
|
152
|
+
* @private
|
|
153
|
+
*/
|
|
154
|
+
private applyArrayItemValidators;
|
|
155
|
+
}
|
|
156
|
+
export {};
|