@reformer/core 1.1.0 → 2.0.0-beta.11
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/dist/behaviors/compute-from.d.ts +2 -0
- package/dist/behaviors/compute-from.js +31 -0
- package/dist/behaviors/copy-from.d.ts +2 -0
- package/dist/behaviors/copy-from.js +29 -0
- package/dist/behaviors/enable-when.d.ts +2 -0
- package/dist/behaviors/enable-when.js +25 -0
- package/dist/behaviors/reset-when.d.ts +2 -0
- package/dist/behaviors/reset-when.js +24 -0
- package/dist/behaviors/revalidate-when.d.ts +2 -0
- package/dist/behaviors/revalidate-when.js +18 -0
- package/dist/behaviors/sync-fields.d.ts +2 -0
- package/dist/behaviors/sync-fields.js +41 -0
- package/dist/behaviors/transform-value.d.ts +2 -0
- package/dist/behaviors/transform-value.js +45 -0
- package/dist/behaviors/watch-field.d.ts +2 -0
- package/dist/behaviors/watch-field.js +21 -0
- package/dist/behaviors.d.ts +6 -2
- package/dist/behaviors.js +27 -228
- package/dist/core/behavior/behavior-context.d.ts +32 -14
- package/dist/core/behavior/behavior-registry.d.ts +15 -27
- package/dist/core/behavior/behaviors/compute-from.d.ts +50 -21
- package/dist/core/behavior/behaviors/copy-from.d.ts +39 -14
- package/dist/core/behavior/behaviors/enable-when.d.ts +88 -19
- package/dist/core/behavior/behaviors/reset-when.d.ts +31 -18
- package/dist/core/behavior/behaviors/revalidate-when.d.ts +40 -17
- package/dist/core/behavior/behaviors/sync-fields.d.ts +34 -14
- package/dist/core/behavior/behaviors/transform-value.d.ts +116 -44
- package/dist/core/behavior/behaviors/watch-field.d.ts +66 -21
- package/dist/core/behavior/compose-behavior.d.ts +2 -12
- package/dist/core/behavior/index.d.ts +0 -1
- package/dist/core/behavior/types.d.ts +2 -8
- package/dist/core/factories/node-factory.d.ts +6 -29
- package/dist/core/nodes/array-node.d.ts +42 -22
- package/dist/core/nodes/field-node.d.ts +51 -26
- package/dist/core/nodes/form-node.d.ts +18 -20
- package/dist/core/nodes/group-node.d.ts +37 -212
- package/dist/core/types/deep-schema.d.ts +2 -12
- package/dist/core/types/field-path.d.ts +1 -1
- package/dist/core/types/form-context.d.ts +36 -30
- package/dist/core/types/{group-node-proxy.d.ts → form-proxy.d.ts} +12 -42
- package/dist/core/types/index.d.ts +52 -6
- package/dist/core/types/validation-schema.d.ts +3 -12
- package/dist/core/utils/abstract-registry.d.ts +74 -0
- package/dist/core/utils/aggregate-signals.d.ts +71 -0
- package/dist/core/utils/create-form.d.ts +3 -20
- package/dist/core/utils/error-handler.d.ts +1 -18
- package/dist/core/utils/field-path-navigator.d.ts +1 -1
- package/dist/core/{validation → utils}/field-path.d.ts +23 -6
- package/dist/core/utils/form-observer.d.ts +176 -0
- package/dist/core/utils/form-proxy-builder.d.ts +25 -0
- package/dist/core/utils/form-submitter.d.ts +121 -0
- package/dist/core/utils/index.d.ts +10 -2
- package/dist/core/utils/registry-helpers.d.ts +0 -7
- package/dist/core/utils/safe-effect.d.ts +73 -0
- package/dist/core/utils/status-machine.d.ts +153 -0
- package/dist/core/utils/type-guards.d.ts +5 -23
- package/dist/core/utils/unique-id.d.ts +53 -0
- package/dist/core/validation/core/apply-when.d.ts +3 -9
- package/dist/core/validation/core/apply.d.ts +2 -13
- package/dist/core/validation/core/validate-async.d.ts +2 -8
- package/dist/core/validation/core/validate-tree.d.ts +10 -10
- package/dist/core/validation/core/validate.d.ts +1 -7
- package/dist/core/validation/index.d.ts +8 -2
- package/dist/core/validation/validate-form.d.ts +1 -38
- package/dist/core/validation/validation-applicator.d.ts +2 -21
- package/dist/core/validation/validation-context.d.ts +67 -28
- package/dist/core/validation/validation-registry.d.ts +11 -25
- package/dist/core/validation/validators/array-validators.d.ts +2 -12
- package/dist/core/validation/validators/date-utils.d.ts +26 -0
- package/dist/core/validation/validators/email.d.ts +2 -9
- package/dist/core/validation/validators/future-date.d.ts +35 -0
- package/dist/core/validation/validators/index.d.ts +7 -1
- package/dist/core/validation/validators/is-date.d.ts +36 -0
- package/dist/core/validation/validators/max-age.d.ts +36 -0
- package/dist/core/validation/validators/max-date.d.ts +36 -0
- package/dist/core/validation/validators/max-length.d.ts +3 -10
- package/dist/core/validation/validators/max.d.ts +3 -10
- package/dist/core/validation/validators/min-age.d.ts +36 -0
- package/dist/core/validation/validators/min-date.d.ts +36 -0
- package/dist/core/validation/validators/min-length.d.ts +3 -10
- package/dist/core/validation/validators/min.d.ts +3 -10
- package/dist/core/validation/validators/number.d.ts +2 -9
- package/dist/core/validation/validators/past-date.d.ts +35 -0
- package/dist/core/validation/validators/pattern.d.ts +2 -9
- package/dist/core/validation/validators/phone.d.ts +2 -9
- package/dist/core/validation/validators/required.d.ts +2 -9
- package/dist/core/validation/validators/url.d.ts +2 -9
- package/dist/date-utils-xUWFslTj.js +29 -0
- package/dist/field-path-DuKdGcIE.js +66 -0
- package/dist/hooks/types.d.ts +328 -0
- package/dist/hooks/useArrayLength.d.ts +31 -0
- package/dist/hooks/useFormControl.d.ts +15 -39
- package/dist/hooks/useFormControlValue.d.ts +167 -0
- package/dist/hooks/useHiddenCondition.d.ts +25 -0
- package/dist/hooks/useSignalSubscription.d.ts +17 -0
- package/dist/index-D25LsbRm.js +73 -0
- package/dist/index.d.ts +8 -1
- package/dist/index.js +3271 -8
- package/dist/registry-helpers-Bv_BJ1s-.js +615 -0
- package/dist/safe-effect-Dh8uw81c.js +20 -0
- package/dist/validate-C3XiA_zf.js +10 -0
- package/dist/validators/email.d.ts +2 -0
- package/dist/validators/email.js +13 -0
- package/dist/validators/future-date.d.ts +2 -0
- package/dist/validators/future-date.js +20 -0
- package/dist/validators/is-date.d.ts +2 -0
- package/dist/validators/is-date.js +12 -0
- package/dist/validators/max-age.d.ts +2 -0
- package/dist/validators/max-age.js +20 -0
- package/dist/validators/max-date.d.ts +2 -0
- package/dist/validators/max-date.js +20 -0
- package/dist/validators/max-length.d.ts +2 -0
- package/dist/validators/max-length.js +11 -0
- package/dist/validators/max.d.ts +2 -0
- package/dist/validators/max.js +11 -0
- package/dist/validators/min-age.d.ts +2 -0
- package/dist/validators/min-age.js +20 -0
- package/dist/validators/min-date.d.ts +2 -0
- package/dist/validators/min-date.js +20 -0
- package/dist/validators/min-length.d.ts +2 -0
- package/dist/validators/min-length.js +11 -0
- package/dist/validators/min.d.ts +2 -0
- package/dist/validators/min.js +11 -0
- package/dist/validators/number.d.ts +2 -0
- package/dist/validators/number.js +35 -0
- package/dist/validators/past-date.d.ts +2 -0
- package/dist/validators/past-date.js +20 -0
- package/dist/validators/pattern.d.ts +2 -0
- package/dist/validators/pattern.js +11 -0
- package/dist/validators/phone.d.ts +2 -0
- package/dist/validators/phone.js +35 -0
- package/dist/validators/required.d.ts +2 -0
- package/dist/validators/required.js +15 -0
- package/dist/validators/url.d.ts +2 -0
- package/dist/validators/url.js +19 -0
- package/dist/validators-BGsNOgT1.js +207 -0
- package/dist/validators.d.ts +6 -2
- package/dist/validators.js +54 -296
- package/llms.txt +8887 -59
- package/package.json +87 -8
- package/dist/core/behavior/behavior-applicator.d.ts +0 -71
- package/dist/core/behavior/behavior-applicator.js +0 -92
- package/dist/core/behavior/behavior-context.js +0 -38
- package/dist/core/behavior/behavior-registry.js +0 -198
- package/dist/core/behavior/behaviors/compute-from.js +0 -84
- package/dist/core/behavior/behaviors/copy-from.js +0 -64
- package/dist/core/behavior/behaviors/enable-when.js +0 -81
- package/dist/core/behavior/behaviors/index.js +0 -11
- package/dist/core/behavior/behaviors/reset-when.js +0 -63
- package/dist/core/behavior/behaviors/revalidate-when.js +0 -51
- package/dist/core/behavior/behaviors/sync-fields.js +0 -66
- package/dist/core/behavior/behaviors/transform-value.js +0 -110
- package/dist/core/behavior/behaviors/watch-field.js +0 -56
- package/dist/core/behavior/compose-behavior.js +0 -166
- package/dist/core/behavior/create-field-path.d.ts +0 -20
- package/dist/core/behavior/create-field-path.js +0 -69
- package/dist/core/behavior/index.js +0 -17
- package/dist/core/behavior/types.js +0 -7
- package/dist/core/context/form-context-impl.d.ts +0 -29
- package/dist/core/context/form-context-impl.js +0 -37
- package/dist/core/factories/index.js +0 -6
- package/dist/core/factories/node-factory.js +0 -281
- package/dist/core/nodes/array-node.js +0 -534
- package/dist/core/nodes/field-node.js +0 -510
- package/dist/core/nodes/form-node.js +0 -343
- package/dist/core/nodes/group-node/field-registry.d.ts +0 -191
- package/dist/core/nodes/group-node/field-registry.js +0 -215
- package/dist/core/nodes/group-node/index.d.ts +0 -11
- package/dist/core/nodes/group-node/index.js +0 -11
- package/dist/core/nodes/group-node/proxy-builder.d.ts +0 -71
- package/dist/core/nodes/group-node/proxy-builder.js +0 -161
- package/dist/core/nodes/group-node/state-manager.d.ts +0 -184
- package/dist/core/nodes/group-node/state-manager.js +0 -265
- package/dist/core/nodes/group-node.js +0 -770
- package/dist/core/types/deep-schema.js +0 -11
- package/dist/core/types/field-path.js +0 -4
- package/dist/core/types/form-context.js +0 -25
- package/dist/core/types/group-node-proxy.js +0 -31
- package/dist/core/types/index.js +0 -4
- package/dist/core/types/validation-schema.js +0 -10
- package/dist/core/utils/create-form.js +0 -24
- package/dist/core/utils/debounce.d.ts +0 -160
- package/dist/core/utils/debounce.js +0 -197
- package/dist/core/utils/error-handler.js +0 -226
- package/dist/core/utils/field-path-navigator.js +0 -374
- package/dist/core/utils/index.js +0 -14
- package/dist/core/utils/registry-helpers.js +0 -79
- package/dist/core/utils/registry-stack.js +0 -86
- package/dist/core/utils/resources.d.ts +0 -41
- package/dist/core/utils/resources.js +0 -69
- package/dist/core/utils/subscription-manager.js +0 -214
- package/dist/core/utils/type-guards.js +0 -169
- package/dist/core/validation/core/apply-when.js +0 -41
- package/dist/core/validation/core/apply.js +0 -38
- package/dist/core/validation/core/index.js +0 -8
- package/dist/core/validation/core/validate-async.js +0 -45
- package/dist/core/validation/core/validate-tree.js +0 -37
- package/dist/core/validation/core/validate.js +0 -38
- package/dist/core/validation/field-path.js +0 -147
- package/dist/core/validation/index.js +0 -33
- package/dist/core/validation/validate-form.js +0 -152
- package/dist/core/validation/validation-applicator.js +0 -217
- package/dist/core/validation/validation-context.js +0 -75
- package/dist/core/validation/validation-registry.js +0 -298
- package/dist/core/validation/validators/array-validators.js +0 -86
- package/dist/core/validation/validators/date.d.ts +0 -38
- package/dist/core/validation/validators/date.js +0 -117
- package/dist/core/validation/validators/email.js +0 -60
- package/dist/core/validation/validators/index.js +0 -14
- package/dist/core/validation/validators/max-length.js +0 -60
- package/dist/core/validation/validators/max.js +0 -60
- package/dist/core/validation/validators/min-length.js +0 -60
- package/dist/core/validation/validators/min.js +0 -60
- package/dist/core/validation/validators/number.js +0 -90
- package/dist/core/validation/validators/pattern.js +0 -62
- package/dist/core/validation/validators/phone.js +0 -58
- package/dist/core/validation/validators/required.js +0 -69
- package/dist/core/validation/validators/url.js +0 -55
- package/dist/create-field-path-CdPF3lIK.js +0 -704
- package/dist/hooks/useFormControl.js +0 -298
- package/dist/node-factory-D7DOnSSN.js +0 -3200
|
@@ -1,226 +0,0 @@
|
|
|
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
|
-
/**
|
|
19
|
-
* Стратегия обработки ошибок
|
|
20
|
-
*
|
|
21
|
-
* Определяет, что делать с ошибкой после логирования
|
|
22
|
-
*/
|
|
23
|
-
export var ErrorStrategy;
|
|
24
|
-
(function (ErrorStrategy) {
|
|
25
|
-
/**
|
|
26
|
-
* Пробросить ошибку дальше (throw)
|
|
27
|
-
* Используется когда ошибка критична и должна остановить выполнение
|
|
28
|
-
*/
|
|
29
|
-
ErrorStrategy["THROW"] = "throw";
|
|
30
|
-
/**
|
|
31
|
-
* Залогировать и проглотить ошибку (продолжить выполнение)
|
|
32
|
-
* Используется когда ошибка не критична
|
|
33
|
-
*/
|
|
34
|
-
ErrorStrategy["LOG"] = "log";
|
|
35
|
-
/**
|
|
36
|
-
* Конвертировать ошибку в ValidationError
|
|
37
|
-
* Используется в async validators для отображения ошибки валидации пользователю
|
|
38
|
-
*/
|
|
39
|
-
ErrorStrategy["CONVERT"] = "convert";
|
|
40
|
-
})(ErrorStrategy || (ErrorStrategy = {}));
|
|
41
|
-
/**
|
|
42
|
-
* Централизованный обработчик ошибок для форм
|
|
43
|
-
*
|
|
44
|
-
* Обеспечивает:
|
|
45
|
-
* - Единообразное логирование ошибок в DEV режиме
|
|
46
|
-
* - Гибкие стратегии обработки (throw/log/convert)
|
|
47
|
-
* - Типобезопасное извлечение сообщений из Error/string/unknown
|
|
48
|
-
*
|
|
49
|
-
* @example
|
|
50
|
-
* ```typescript
|
|
51
|
-
* // В async validator (конвертировать в ValidationError)
|
|
52
|
-
* try {
|
|
53
|
-
* await validateEmail(value);
|
|
54
|
-
* } catch (error) {
|
|
55
|
-
* return FormErrorHandler.handle(error, 'EmailValidator', ErrorStrategy.CONVERT);
|
|
56
|
-
* }
|
|
57
|
-
*
|
|
58
|
-
* // В behavior applicator (пробросить критичную ошибку)
|
|
59
|
-
* try {
|
|
60
|
-
* applyBehavior(schema);
|
|
61
|
-
* } catch (error) {
|
|
62
|
-
* FormErrorHandler.handle(error, 'BehaviorApplicator', ErrorStrategy.THROW);
|
|
63
|
-
* }
|
|
64
|
-
*
|
|
65
|
-
* // В validator (залогировать и продолжить)
|
|
66
|
-
* try {
|
|
67
|
-
* validator(value);
|
|
68
|
-
* } catch (error) {
|
|
69
|
-
* FormErrorHandler.handle(error, 'Validator', ErrorStrategy.LOG);
|
|
70
|
-
* }
|
|
71
|
-
* ```
|
|
72
|
-
*/
|
|
73
|
-
export class FormErrorHandler {
|
|
74
|
-
/**
|
|
75
|
-
* Обработать ошибку согласно заданной стратегии
|
|
76
|
-
*
|
|
77
|
-
* @param error Ошибка для обработки (Error | string | unknown)
|
|
78
|
-
* @param context Контекст ошибки для логирования (например, 'AsyncValidator', 'BehaviorRegistry')
|
|
79
|
-
* @param strategy Стратегия обработки (THROW | LOG | CONVERT)
|
|
80
|
-
* @returns ValidationError если strategy = CONVERT, undefined если strategy = LOG, никогда не возвращается если strategy = THROW
|
|
81
|
-
*
|
|
82
|
-
* @example
|
|
83
|
-
* ```typescript
|
|
84
|
-
* // THROW - пробросить ошибку
|
|
85
|
-
* try {
|
|
86
|
-
* riskyOperation();
|
|
87
|
-
* } catch (error) {
|
|
88
|
-
* FormErrorHandler.handle(error, 'RiskyOperation', ErrorStrategy.THROW);
|
|
89
|
-
* // Этот код никогда не выполнится
|
|
90
|
-
* }
|
|
91
|
-
*
|
|
92
|
-
* // LOG - залогировать и продолжить
|
|
93
|
-
* try {
|
|
94
|
-
* nonCriticalOperation();
|
|
95
|
-
* } catch (error) {
|
|
96
|
-
* FormErrorHandler.handle(error, 'NonCritical', ErrorStrategy.LOG);
|
|
97
|
-
* // Продолжаем выполнение
|
|
98
|
-
* }
|
|
99
|
-
*
|
|
100
|
-
* // CONVERT - конвертировать в ValidationError
|
|
101
|
-
* try {
|
|
102
|
-
* await validator(value);
|
|
103
|
-
* } catch (error) {
|
|
104
|
-
* const validationError = FormErrorHandler.handle(
|
|
105
|
-
* error,
|
|
106
|
-
* 'AsyncValidator',
|
|
107
|
-
* ErrorStrategy.CONVERT
|
|
108
|
-
* );
|
|
109
|
-
* return validationError;
|
|
110
|
-
* }
|
|
111
|
-
* ```
|
|
112
|
-
*/
|
|
113
|
-
static handle(error, context, strategy = ErrorStrategy.THROW) {
|
|
114
|
-
// Извлекаем сообщение из ошибки
|
|
115
|
-
const message = this.extractMessage(error);
|
|
116
|
-
// Логируем в DEV режиме
|
|
117
|
-
if (import.meta.env.DEV) {
|
|
118
|
-
console.error(`[${context}]`, error);
|
|
119
|
-
}
|
|
120
|
-
// Применяем стратегию
|
|
121
|
-
switch (strategy) {
|
|
122
|
-
case ErrorStrategy.THROW:
|
|
123
|
-
throw error;
|
|
124
|
-
case ErrorStrategy.LOG:
|
|
125
|
-
// Просто логируем, не возвращаем ничего
|
|
126
|
-
return;
|
|
127
|
-
case ErrorStrategy.CONVERT:
|
|
128
|
-
// Конвертируем в ValidationError
|
|
129
|
-
return {
|
|
130
|
-
code: 'validator_error',
|
|
131
|
-
message,
|
|
132
|
-
params: { field: context },
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Извлечь сообщение из ошибки
|
|
138
|
-
*
|
|
139
|
-
* Обрабатывает различные типы ошибок:
|
|
140
|
-
* - Error объекты → error.message
|
|
141
|
-
* - Строки → возвращает как есть
|
|
142
|
-
* - Объекты с message → извлекает message
|
|
143
|
-
* - Другое → String(error)
|
|
144
|
-
*
|
|
145
|
-
* @param error Ошибка для извлечения сообщения
|
|
146
|
-
* @returns Сообщение ошибки
|
|
147
|
-
* @private
|
|
148
|
-
*
|
|
149
|
-
* @example
|
|
150
|
-
* ```typescript
|
|
151
|
-
* FormErrorHandler.extractMessage(new Error('Test'));
|
|
152
|
-
* // 'Test'
|
|
153
|
-
*
|
|
154
|
-
* FormErrorHandler.extractMessage('String error');
|
|
155
|
-
* // 'String error'
|
|
156
|
-
*
|
|
157
|
-
* FormErrorHandler.extractMessage({ message: 'Object error' });
|
|
158
|
-
* // 'Object error'
|
|
159
|
-
*
|
|
160
|
-
* FormErrorHandler.extractMessage(null);
|
|
161
|
-
* // 'null'
|
|
162
|
-
* ```
|
|
163
|
-
*/
|
|
164
|
-
static extractMessage(error) {
|
|
165
|
-
if (error instanceof Error) {
|
|
166
|
-
return error.message;
|
|
167
|
-
}
|
|
168
|
-
if (typeof error === 'string') {
|
|
169
|
-
return error;
|
|
170
|
-
}
|
|
171
|
-
if (typeof error === 'object' && error !== null && 'message' in error) {
|
|
172
|
-
return String(error.message);
|
|
173
|
-
}
|
|
174
|
-
return String(error);
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* Создать ValidationError с заданными параметрами
|
|
178
|
-
*
|
|
179
|
-
* Утилитная функция для создания ValidationError объектов
|
|
180
|
-
*
|
|
181
|
-
* @param code Код ошибки
|
|
182
|
-
* @param message Сообщение ошибки
|
|
183
|
-
* @param field Поле (опционально)
|
|
184
|
-
* @returns ValidationError объект
|
|
185
|
-
*
|
|
186
|
-
* @example
|
|
187
|
-
* ```typescript
|
|
188
|
-
* const error = FormErrorHandler.createValidationError(
|
|
189
|
-
* 'required',
|
|
190
|
-
* 'This field is required',
|
|
191
|
-
* 'email'
|
|
192
|
-
* );
|
|
193
|
-
* // { code: 'required', message: 'This field is required', field: 'email' }
|
|
194
|
-
* ```
|
|
195
|
-
*/
|
|
196
|
-
static createValidationError(code, message, field) {
|
|
197
|
-
return {
|
|
198
|
-
code,
|
|
199
|
-
message,
|
|
200
|
-
params: field ? { field } : undefined,
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
/**
|
|
204
|
-
* Проверить, является ли объект ValidationError
|
|
205
|
-
*
|
|
206
|
-
* Type guard для ValidationError
|
|
207
|
-
*
|
|
208
|
-
* @param value Значение для проверки
|
|
209
|
-
* @returns true если value является ValidationError
|
|
210
|
-
*
|
|
211
|
-
* @example
|
|
212
|
-
* ```typescript
|
|
213
|
-
* if (FormErrorHandler.isValidationError(result)) {
|
|
214
|
-
* console.log(result.code); // OK, типобезопасно
|
|
215
|
-
* }
|
|
216
|
-
* ```
|
|
217
|
-
*/
|
|
218
|
-
static isValidationError(value) {
|
|
219
|
-
return (typeof value === 'object' &&
|
|
220
|
-
value !== null &&
|
|
221
|
-
'code' in value &&
|
|
222
|
-
'message' in value &&
|
|
223
|
-
typeof value.code === 'string' &&
|
|
224
|
-
typeof value.message === 'string');
|
|
225
|
-
}
|
|
226
|
-
}
|
|
@@ -1,374 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Навигация по путям к полям формы
|
|
3
|
-
*
|
|
4
|
-
* Централизует логику парсинга и навигации по путям к полям формы.
|
|
5
|
-
* Используется в ValidationContext, BehaviorContext, GroupNode для единообразной
|
|
6
|
-
* обработки путей вида "address.city" или "items[0].name".
|
|
7
|
-
*
|
|
8
|
-
* Устраняет дублирование логики парсинга путей в 4 местах кодовой базы.
|
|
9
|
-
*
|
|
10
|
-
* @internal
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```typescript
|
|
14
|
-
* const navigator = new FieldPathNavigator();
|
|
15
|
-
*
|
|
16
|
-
* // Парсинг пути
|
|
17
|
-
* const segments = navigator.parsePath('items[0].title');
|
|
18
|
-
* // [{ key: 'items', index: 0 }, { key: 'title' }]
|
|
19
|
-
*
|
|
20
|
-
* // Получение значения из объекта
|
|
21
|
-
* const obj = { items: [{ title: 'Item 1' }] };
|
|
22
|
-
* const value = navigator.getValueByPath(obj, 'items[0].title');
|
|
23
|
-
* // 'Item 1'
|
|
24
|
-
*
|
|
25
|
-
* // Получение узла формы
|
|
26
|
-
* const titleNode = navigator.getNodeByPath(form, 'items[0].title');
|
|
27
|
-
* titleNode?.setValue('New Title');
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
|
-
export class FieldPathNavigator {
|
|
31
|
-
/**
|
|
32
|
-
* Парсит путь в массив сегментов
|
|
33
|
-
*
|
|
34
|
-
* Поддерживаемые форматы:
|
|
35
|
-
* - Простые пути: "name", "email"
|
|
36
|
-
* - Вложенные пути: "address.city", "user.profile.avatar"
|
|
37
|
-
* - Массивы: "items[0]", "items[0].name", "tags[1][0]"
|
|
38
|
-
* - Комбинации: "orders[0].items[1].price"
|
|
39
|
-
*
|
|
40
|
-
* @param path Путь к полю (строка с точками и квадратными скобками)
|
|
41
|
-
* @returns Массив сегментов пути
|
|
42
|
-
*
|
|
43
|
-
* @example
|
|
44
|
-
* ```typescript
|
|
45
|
-
* navigator.parsePath('email');
|
|
46
|
-
* // [{ key: 'email' }]
|
|
47
|
-
*
|
|
48
|
-
* navigator.parsePath('address.city');
|
|
49
|
-
* // [{ key: 'address' }, { key: 'city' }]
|
|
50
|
-
*
|
|
51
|
-
* navigator.parsePath('items[0].name');
|
|
52
|
-
* // [{ key: 'items', index: 0 }, { key: 'name' }]
|
|
53
|
-
* ```
|
|
54
|
-
*/
|
|
55
|
-
parsePath(path) {
|
|
56
|
-
const segments = [];
|
|
57
|
-
let currentPart = '';
|
|
58
|
-
let inBrackets = false;
|
|
59
|
-
for (let i = 0; i < path.length; i++) {
|
|
60
|
-
const char = path[i];
|
|
61
|
-
if (char === '[') {
|
|
62
|
-
inBrackets = true;
|
|
63
|
-
currentPart += char;
|
|
64
|
-
}
|
|
65
|
-
else if (char === ']') {
|
|
66
|
-
inBrackets = false;
|
|
67
|
-
currentPart += char;
|
|
68
|
-
}
|
|
69
|
-
else if (char === '.' && !inBrackets) {
|
|
70
|
-
// Разделитель найден, обрабатываем накопленную часть
|
|
71
|
-
if (currentPart) {
|
|
72
|
-
this.addSegment(segments, currentPart);
|
|
73
|
-
currentPart = '';
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
currentPart += char;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
// Добавляем последнюю часть
|
|
81
|
-
if (currentPart) {
|
|
82
|
-
this.addSegment(segments, currentPart);
|
|
83
|
-
}
|
|
84
|
-
return segments;
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Добавляет сегмент в массив, обрабатывая массивы
|
|
88
|
-
* @private
|
|
89
|
-
*/
|
|
90
|
-
addSegment(segments, part) {
|
|
91
|
-
// Проверка на массив: items[0]
|
|
92
|
-
const arrayMatch = part.match(/^(.+)\[(\d+)\]$/);
|
|
93
|
-
if (arrayMatch) {
|
|
94
|
-
segments.push({
|
|
95
|
-
key: arrayMatch[1],
|
|
96
|
-
index: parseInt(arrayMatch[2], 10),
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
else {
|
|
100
|
-
segments.push({ key: part });
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Получает значение по пути из объекта
|
|
105
|
-
*
|
|
106
|
-
* Проходит по всем сегментам пути и возвращает конечное значение.
|
|
107
|
-
* Если путь не найден, возвращает undefined.
|
|
108
|
-
*
|
|
109
|
-
* @param obj Объект для навигации
|
|
110
|
-
* @param path Путь к значению
|
|
111
|
-
* @returns Значение или undefined, если путь не найден
|
|
112
|
-
*
|
|
113
|
-
* @example
|
|
114
|
-
* ```typescript
|
|
115
|
-
* const obj = {
|
|
116
|
-
* email: 'test@mail.com',
|
|
117
|
-
* address: { city: 'Moscow' },
|
|
118
|
-
* items: [{ title: 'Item 1' }]
|
|
119
|
-
* };
|
|
120
|
-
*
|
|
121
|
-
* navigator.getValueByPath(obj, 'email');
|
|
122
|
-
* // 'test@mail.com'
|
|
123
|
-
*
|
|
124
|
-
* navigator.getValueByPath(obj, 'address.city');
|
|
125
|
-
* // 'Moscow'
|
|
126
|
-
*
|
|
127
|
-
* navigator.getValueByPath(obj, 'items[0].title');
|
|
128
|
-
* // 'Item 1'
|
|
129
|
-
*
|
|
130
|
-
* navigator.getValueByPath(obj, 'invalid.path');
|
|
131
|
-
* // undefined
|
|
132
|
-
* ```
|
|
133
|
-
*/
|
|
134
|
-
getValueByPath(obj, path) {
|
|
135
|
-
const segments = this.parsePath(path);
|
|
136
|
-
let current = obj;
|
|
137
|
-
for (const segment of segments) {
|
|
138
|
-
if (current == null)
|
|
139
|
-
return undefined;
|
|
140
|
-
current = current[segment.key];
|
|
141
|
-
if (segment.index !== undefined) {
|
|
142
|
-
if (!Array.isArray(current))
|
|
143
|
-
return undefined;
|
|
144
|
-
current = current[segment.index];
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
return current;
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* Устанавливает значение по пути в объекте (мутирует объект)
|
|
151
|
-
*
|
|
152
|
-
* Создает промежуточные объекты, если они не существуют.
|
|
153
|
-
* Выбрасывает ошибку, если ожидается массив, но его нет.
|
|
154
|
-
*
|
|
155
|
-
* @param obj Объект для модификации
|
|
156
|
-
* @param path Путь к значению
|
|
157
|
-
* @param value Новое значение
|
|
158
|
-
*
|
|
159
|
-
* @throws {Error} Если ожидается массив по пути, но его нет
|
|
160
|
-
*
|
|
161
|
-
* @example
|
|
162
|
-
* ```typescript
|
|
163
|
-
* const obj = { address: { city: '' } };
|
|
164
|
-
* navigator.setValueByPath(obj, 'address.city', 'Moscow');
|
|
165
|
-
* // obj.address.city === 'Moscow'
|
|
166
|
-
*
|
|
167
|
-
* const obj2: UnknownRecord = {};
|
|
168
|
-
* navigator.setValueByPath(obj2, 'address.city', 'Moscow');
|
|
169
|
-
* // Создаст { address: { city: 'Moscow' } }
|
|
170
|
-
*
|
|
171
|
-
* const obj3 = { items: [{ title: 'Old' }] };
|
|
172
|
-
* navigator.setValueByPath(obj3, 'items[0].title', 'New');
|
|
173
|
-
* // obj3.items[0].title === 'New'
|
|
174
|
-
* ```
|
|
175
|
-
*/
|
|
176
|
-
setValueByPath(obj, path, value) {
|
|
177
|
-
const segments = this.parsePath(path);
|
|
178
|
-
if (segments.length === 0) {
|
|
179
|
-
throw new Error('Cannot set value: empty path');
|
|
180
|
-
}
|
|
181
|
-
let current = obj;
|
|
182
|
-
// Проходим до предпоследнего сегмента
|
|
183
|
-
for (let i = 0; i < segments.length - 1; i++) {
|
|
184
|
-
const segment = segments[i];
|
|
185
|
-
let next = current[segment.key];
|
|
186
|
-
if (segment.index !== undefined) {
|
|
187
|
-
// Доступ к массиву: items[0]
|
|
188
|
-
if (!Array.isArray(next)) {
|
|
189
|
-
throw new Error(`Expected array at path segment: ${segment.key}, but got ${typeof next}`);
|
|
190
|
-
}
|
|
191
|
-
current = next[segment.index];
|
|
192
|
-
}
|
|
193
|
-
else {
|
|
194
|
-
// Доступ к объекту: address
|
|
195
|
-
if (next == null) {
|
|
196
|
-
// Создаем объект, если его нет
|
|
197
|
-
current[segment.key] = {};
|
|
198
|
-
next = current[segment.key];
|
|
199
|
-
}
|
|
200
|
-
current = next;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
// Устанавливаем значение в последнем сегменте
|
|
204
|
-
const lastSegment = segments[segments.length - 1];
|
|
205
|
-
if (lastSegment.index !== undefined) {
|
|
206
|
-
const arr = current[lastSegment.key];
|
|
207
|
-
if (!Array.isArray(arr)) {
|
|
208
|
-
throw new Error(`Expected array at path segment: ${lastSegment.key}, but got ${typeof arr}`);
|
|
209
|
-
}
|
|
210
|
-
arr[lastSegment.index] = value;
|
|
211
|
-
}
|
|
212
|
-
else {
|
|
213
|
-
current[lastSegment.key] = value;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
/**
|
|
217
|
-
* Получить значение из FormNode по пути
|
|
218
|
-
*
|
|
219
|
-
* Автоматически извлекает значение из FormNode (через .value.value).
|
|
220
|
-
* Используется в ValidationContext и BehaviorContext для единообразного
|
|
221
|
-
* доступа к значениям полей формы.
|
|
222
|
-
*
|
|
223
|
-
* @param form Корневой узел формы (обычно GroupNode)
|
|
224
|
-
* @param path Путь к полю
|
|
225
|
-
* @returns Значение поля или undefined, если путь не найден
|
|
226
|
-
*
|
|
227
|
-
* @example
|
|
228
|
-
* ```typescript
|
|
229
|
-
* const form = new GroupNode({
|
|
230
|
-
* email: { value: 'test@mail.com', component: Input },
|
|
231
|
-
* address: {
|
|
232
|
-
* city: { value: 'Moscow', component: Input }
|
|
233
|
-
* },
|
|
234
|
-
* items: [{ title: { value: 'Item 1', component: Input } }]
|
|
235
|
-
* });
|
|
236
|
-
*
|
|
237
|
-
* navigator.getFormNodeValue(form, 'email');
|
|
238
|
-
* // 'test@mail.com'
|
|
239
|
-
*
|
|
240
|
-
* navigator.getFormNodeValue(form, 'address.city');
|
|
241
|
-
* // 'Moscow'
|
|
242
|
-
*
|
|
243
|
-
* navigator.getFormNodeValue(form, 'items[0].title');
|
|
244
|
-
* // 'Item 1'
|
|
245
|
-
*
|
|
246
|
-
* navigator.getFormNodeValue(form, 'invalid.path');
|
|
247
|
-
* // undefined
|
|
248
|
-
* ```
|
|
249
|
-
*/
|
|
250
|
-
getFormNodeValue(form, path) {
|
|
251
|
-
const node = this.getNodeByPath(form, path);
|
|
252
|
-
if (node == null) {
|
|
253
|
-
return undefined;
|
|
254
|
-
}
|
|
255
|
-
// FormNode возвращает .value.value
|
|
256
|
-
if (this.isFormNode(node)) {
|
|
257
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
258
|
-
return node.value.value;
|
|
259
|
-
}
|
|
260
|
-
// Для обычных объектов возвращаем как есть
|
|
261
|
-
return node;
|
|
262
|
-
}
|
|
263
|
-
/**
|
|
264
|
-
* Type guard для проверки, является ли объект FormNode
|
|
265
|
-
*
|
|
266
|
-
* Проверяет наличие характерных свойств FormNode:
|
|
267
|
-
* - value (Signal)
|
|
268
|
-
* - value.value (значение Signal)
|
|
269
|
-
*
|
|
270
|
-
* @param obj Объект для проверки
|
|
271
|
-
* @returns true, если объект является FormNode
|
|
272
|
-
* @private
|
|
273
|
-
*/
|
|
274
|
-
isFormNode(obj) {
|
|
275
|
-
return (obj != null &&
|
|
276
|
-
typeof obj === 'object' &&
|
|
277
|
-
'value' in obj &&
|
|
278
|
-
typeof obj.value === 'object' &&
|
|
279
|
-
obj.value != null &&
|
|
280
|
-
'value' in obj.value);
|
|
281
|
-
}
|
|
282
|
-
/**
|
|
283
|
-
* Получает узел формы по пути
|
|
284
|
-
*
|
|
285
|
-
* Навигирует по структуре FormNode (GroupNode/FieldNode/ArrayNode)
|
|
286
|
-
* и возвращает узел по указанному пути.
|
|
287
|
-
*
|
|
288
|
-
* Поддерживает:
|
|
289
|
-
* - Доступ к полям GroupNode через fields Map
|
|
290
|
-
* - Доступ к элементам ArrayNode через индекс
|
|
291
|
-
* - Proxy-доступ к полям (для обратной совместимости)
|
|
292
|
-
*
|
|
293
|
-
* @param form Корневой узел формы (обычно GroupNode)
|
|
294
|
-
* @param path Путь к узлу
|
|
295
|
-
* @returns Узел формы или null, если путь не найден
|
|
296
|
-
*
|
|
297
|
-
* @example
|
|
298
|
-
* ```typescript
|
|
299
|
-
* const form = new GroupNode({
|
|
300
|
-
* email: { value: '', component: Input },
|
|
301
|
-
* address: {
|
|
302
|
-
* city: { value: '', component: Input }
|
|
303
|
-
* },
|
|
304
|
-
* items: [{ title: { value: '', component: Input } }]
|
|
305
|
-
* });
|
|
306
|
-
*
|
|
307
|
-
* const emailNode = navigator.getNodeByPath(form, 'email');
|
|
308
|
-
* // FieldNode
|
|
309
|
-
*
|
|
310
|
-
* const cityNode = navigator.getNodeByPath(form, 'address.city');
|
|
311
|
-
* // FieldNode
|
|
312
|
-
*
|
|
313
|
-
* const itemNode = navigator.getNodeByPath(form, 'items[0]');
|
|
314
|
-
* // GroupNode
|
|
315
|
-
*
|
|
316
|
-
* const titleNode = navigator.getNodeByPath(form, 'items[0].title');
|
|
317
|
-
* // FieldNode
|
|
318
|
-
*
|
|
319
|
-
* const invalidNode = navigator.getNodeByPath(form, 'invalid.path');
|
|
320
|
-
* // null
|
|
321
|
-
* ```
|
|
322
|
-
*/
|
|
323
|
-
getNodeByPath(form, path) {
|
|
324
|
-
const segments = this.parsePath(path);
|
|
325
|
-
let current = form;
|
|
326
|
-
for (const segment of segments) {
|
|
327
|
-
if (current == null)
|
|
328
|
-
return null;
|
|
329
|
-
const currentRecord = current;
|
|
330
|
-
// Для GroupNode: доступ через fields Map
|
|
331
|
-
if (currentRecord.fields && currentRecord.fields instanceof Map) {
|
|
332
|
-
current = currentRecord.fields.get(segment.key);
|
|
333
|
-
// Если это не ArrayNode или нет индекса, продолжаем
|
|
334
|
-
if (segment.index === undefined) {
|
|
335
|
-
if (current == null)
|
|
336
|
-
return null;
|
|
337
|
-
continue;
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
// Для ArrayNode: доступ через items и индекс
|
|
341
|
-
else if (segment.index !== undefined && currentRecord.items) {
|
|
342
|
-
const items = currentRecord.items.value || currentRecord.items;
|
|
343
|
-
if (!Array.isArray(items))
|
|
344
|
-
return null;
|
|
345
|
-
current = items[segment.index];
|
|
346
|
-
if (current == null)
|
|
347
|
-
return null;
|
|
348
|
-
continue;
|
|
349
|
-
}
|
|
350
|
-
// Proxy-доступ (для обратной совместимости)
|
|
351
|
-
else if (segment.index === undefined) {
|
|
352
|
-
current = currentRecord[segment.key];
|
|
353
|
-
if (current == null)
|
|
354
|
-
return null;
|
|
355
|
-
continue;
|
|
356
|
-
}
|
|
357
|
-
// Если нашли ArrayNode и есть индекс, получаем элемент массива
|
|
358
|
-
if (current && segment.index !== undefined && current.items) {
|
|
359
|
-
const items = current.items.value ||
|
|
360
|
-
current.items;
|
|
361
|
-
if (!Array.isArray(items))
|
|
362
|
-
return null;
|
|
363
|
-
current = items[segment.index];
|
|
364
|
-
}
|
|
365
|
-
// Если запрашивается индекс, но узел не является ArrayNode, возвращаем null
|
|
366
|
-
else if (current && segment.index !== undefined && !current.items) {
|
|
367
|
-
return null;
|
|
368
|
-
}
|
|
369
|
-
if (current == null)
|
|
370
|
-
return null;
|
|
371
|
-
}
|
|
372
|
-
return current;
|
|
373
|
-
}
|
|
374
|
-
}
|
package/dist/core/utils/index.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Утилиты для работы с формами
|
|
3
|
-
*
|
|
4
|
-
* Централизованные вспомогательные классы и функции.
|
|
5
|
-
*/
|
|
6
|
-
export { FieldPathNavigator } from './field-path-navigator';
|
|
7
|
-
export { SubscriptionManager } from './subscription-manager';
|
|
8
|
-
export { getCurrentValidationRegistry, getCurrentBehaviorRegistry } from './registry-helpers';
|
|
9
|
-
export { RegistryStack } from './registry-stack';
|
|
10
|
-
export { isFormNode, isFieldNode, isGroupNode, isArrayNode, getNodeType } from './type-guards';
|
|
11
|
-
export { Debouncer } from './debounce';
|
|
12
|
-
export { FormErrorHandler, ErrorStrategy } from './error-handler';
|
|
13
|
-
export { createForm } from './create-form';
|
|
14
|
-
export * from './resources';
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Вспомогательные функции для работы с реестрами
|
|
3
|
-
*
|
|
4
|
-
* Централизует логику получения текущего активного реестра валидации/поведения
|
|
5
|
-
* из context stack, избегая дублирования кода в schema-validators.ts,
|
|
6
|
-
* array-validators.ts и schema-behaviors.ts
|
|
7
|
-
*/
|
|
8
|
-
import { ValidationRegistry } from '../validation/validation-registry';
|
|
9
|
-
import { BehaviorRegistry } from '../behavior/behavior-registry';
|
|
10
|
-
/**
|
|
11
|
-
* Получить текущий активный ValidationRegistry из context stack
|
|
12
|
-
*
|
|
13
|
-
* Используется внутри validation schema функций (validate, validateAsync, required и т.д.)
|
|
14
|
-
* для регистрации валидаторов в активном контексте GroupNode
|
|
15
|
-
*
|
|
16
|
-
* @returns Текущий ValidationRegistry или заглушка в production
|
|
17
|
-
* @throws Error если нет активного контекста (только в DEV режиме)
|
|
18
|
-
*
|
|
19
|
-
* @internal
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* ```typescript
|
|
23
|
-
* function required(fieldPath, options) {
|
|
24
|
-
* const registry = getCurrentValidationRegistry();
|
|
25
|
-
* registry.registerSync(path, validator, options);
|
|
26
|
-
* }
|
|
27
|
-
* ```
|
|
28
|
-
*/
|
|
29
|
-
export function getCurrentValidationRegistry() {
|
|
30
|
-
const registry = ValidationRegistry.getCurrent();
|
|
31
|
-
if (!registry) {
|
|
32
|
-
if (import.meta.env.DEV) {
|
|
33
|
-
throw new Error('No active ValidationRegistry context. Make sure to call applyValidationSchema() within a GroupNode context.');
|
|
34
|
-
}
|
|
35
|
-
// В production возвращаем заглушку чтобы не ломать приложение
|
|
36
|
-
return {
|
|
37
|
-
registerSync: () => { },
|
|
38
|
-
registerAsync: () => { },
|
|
39
|
-
registerTree: () => { },
|
|
40
|
-
enterCondition: () => { },
|
|
41
|
-
exitCondition: () => { },
|
|
42
|
-
registerArrayItemValidation: () => { },
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
return registry;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Получить текущий активный BehaviorRegistry из context stack
|
|
49
|
-
*
|
|
50
|
-
* Используется внутри behavior schema функций (copyFrom, enableWhen, computeFrom и т.д.)
|
|
51
|
-
* для регистрации поведений в активном контексте GroupNode
|
|
52
|
-
*
|
|
53
|
-
* @returns Текущий BehaviorRegistry или заглушка в production
|
|
54
|
-
* @throws Error если нет активного контекста (только в DEV режиме)
|
|
55
|
-
*
|
|
56
|
-
* @internal
|
|
57
|
-
*
|
|
58
|
-
* @example
|
|
59
|
-
* ```typescript
|
|
60
|
-
* function copyFrom(target, source, options) {
|
|
61
|
-
* const registry = getCurrentBehaviorRegistry();
|
|
62
|
-
* const handler = createCopyBehavior(target, source, options);
|
|
63
|
-
* registry.register(handler, { debounce: options?.debounce });
|
|
64
|
-
* }
|
|
65
|
-
* ```
|
|
66
|
-
*/
|
|
67
|
-
export function getCurrentBehaviorRegistry() {
|
|
68
|
-
const registry = BehaviorRegistry.getCurrent();
|
|
69
|
-
if (!registry) {
|
|
70
|
-
if (import.meta.env.DEV) {
|
|
71
|
-
throw new Error('No active BehaviorRegistry context. Make sure to call applyBehaviorSchema() within a GroupNode context.');
|
|
72
|
-
}
|
|
73
|
-
// В production возвращаем заглушку чтобы не ломать приложение
|
|
74
|
-
return {
|
|
75
|
-
register: () => { },
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
return registry;
|
|
79
|
-
}
|