@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,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Типы для validation schema паттерна
|
|
3
|
+
*
|
|
4
|
+
* Основано на Angular Signal Forms подходе:
|
|
5
|
+
* - Валидация определяется отдельно от схемы полей
|
|
6
|
+
* - Поддержка условной валидации (applyWhen)
|
|
7
|
+
* - Cross-field валидация (validateTree)
|
|
8
|
+
* - Асинхронная валидация с контекстом
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Фабричная функция для создания формы с правильной типизацией
|
|
3
|
+
*
|
|
4
|
+
* Решает проблему с типизацией конструктора GroupNode, который возвращает
|
|
5
|
+
* Proxy (GroupNodeWithControls), но TypeScript не может это вывести автоматически.
|
|
6
|
+
*
|
|
7
|
+
* @group Utilities
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* // Вместо:
|
|
12
|
+
* const form: GroupNodeWithControls<MyForm> = new GroupNode<MyForm>(config);
|
|
13
|
+
*
|
|
14
|
+
* // Используйте:
|
|
15
|
+
* const form = createForm<MyForm>(config);
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
import type { GroupNodeWithControls, GroupNodeConfig, FormSchema } from '../types';
|
|
19
|
+
/**
|
|
20
|
+
* Создать форму с полной конфигурацией (form, behavior, validation)
|
|
21
|
+
*
|
|
22
|
+
* @group Utilities
|
|
23
|
+
*
|
|
24
|
+
* @param config - Конфигурация формы с полями, поведением и валидацией
|
|
25
|
+
* @returns Типизированная форма с Proxy-доступом к полям
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* const form = createForm<UserForm>({
|
|
30
|
+
* form: {
|
|
31
|
+
* email: { value: '', component: Input },
|
|
32
|
+
* password: { value: '', component: Input },
|
|
33
|
+
* },
|
|
34
|
+
* validation: (path) => {
|
|
35
|
+
* required(path.email);
|
|
36
|
+
* email(path.email);
|
|
37
|
+
* required(path.password);
|
|
38
|
+
* minLength(path.password, 8);
|
|
39
|
+
* },
|
|
40
|
+
* });
|
|
41
|
+
*
|
|
42
|
+
* // TypeScript знает о полях:
|
|
43
|
+
* form.email.setValue('test@mail.com');
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export declare function createForm<T>(config: GroupNodeConfig<T>): GroupNodeWithControls<T>;
|
|
47
|
+
/**
|
|
48
|
+
* Создать форму только со схемой полей (обратная совместимость)
|
|
49
|
+
*
|
|
50
|
+
* @param schema - Схема полей формы
|
|
51
|
+
* @returns Типизированная форма с Proxy-доступом к полям
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```typescript
|
|
55
|
+
* const form = createForm<UserForm>({
|
|
56
|
+
* email: { value: '', component: Input },
|
|
57
|
+
* password: { value: '', component: Input },
|
|
58
|
+
* });
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export declare function createForm<T>(schema: FormSchema<T>): GroupNodeWithControls<T>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Фабричная функция для создания формы с правильной типизацией
|
|
3
|
+
*
|
|
4
|
+
* Решает проблему с типизацией конструктора GroupNode, который возвращает
|
|
5
|
+
* Proxy (GroupNodeWithControls), но TypeScript не может это вывести автоматически.
|
|
6
|
+
*
|
|
7
|
+
* @group Utilities
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* // Вместо:
|
|
12
|
+
* const form: GroupNodeWithControls<MyForm> = new GroupNode<MyForm>(config);
|
|
13
|
+
*
|
|
14
|
+
* // Используйте:
|
|
15
|
+
* const form = createForm<MyForm>(config);
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
import { GroupNode } from '../nodes/group-node';
|
|
19
|
+
/**
|
|
20
|
+
* Реализация фабричной функции
|
|
21
|
+
*/
|
|
22
|
+
export function createForm(schemaOrConfig) {
|
|
23
|
+
return new GroupNode(schemaOrConfig);
|
|
24
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Debouncer - утилита для отложенного выполнения функций
|
|
3
|
+
*
|
|
4
|
+
* Устраняет дублирование debounce логики между field-node.ts и behavior-registry.ts
|
|
5
|
+
*
|
|
6
|
+
* @internal
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const debouncer = new Debouncer(300);
|
|
11
|
+
*
|
|
12
|
+
* // Отложить выполнение на 300мс
|
|
13
|
+
* await debouncer.debounce(async () => {
|
|
14
|
+
* await validateField();
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* // Отменить отложенное выполнение
|
|
18
|
+
* debouncer.cancel();
|
|
19
|
+
*
|
|
20
|
+
* // Выполнить немедленно, отменив отложенное
|
|
21
|
+
* await debouncer.flush(async () => {
|
|
22
|
+
* await validateField();
|
|
23
|
+
* });
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* Класс для debouncing (отложенного выполнения) функций
|
|
28
|
+
*
|
|
29
|
+
* @internal
|
|
30
|
+
*
|
|
31
|
+
* Откладывает выполнение функции на заданное время. Если функция вызывается
|
|
32
|
+
* повторно до истечения времени, предыдущий вызов отменяется.
|
|
33
|
+
*
|
|
34
|
+
* Полезно для:
|
|
35
|
+
* - Отложенной валидации при вводе (debounced validation)
|
|
36
|
+
* - Отложенного сохранения данных
|
|
37
|
+
* - Отложенной обработки событий behaviors
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* // В FieldNode
|
|
42
|
+
* class FieldNode {
|
|
43
|
+
* private validationDebouncer: Debouncer;
|
|
44
|
+
*
|
|
45
|
+
* constructor(config) {
|
|
46
|
+
* this.validationDebouncer = new Debouncer(config.debounce || 0);
|
|
47
|
+
* }
|
|
48
|
+
*
|
|
49
|
+
* async validate(): Promise<boolean> {
|
|
50
|
+
* return this.validationDebouncer.debounce(async () => {
|
|
51
|
+
* // Валидация выполнится через debounce мс
|
|
52
|
+
* return await this.runValidation();
|
|
53
|
+
* });
|
|
54
|
+
* }
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export declare class Debouncer {
|
|
59
|
+
private readonly delay;
|
|
60
|
+
/**
|
|
61
|
+
* Таймер для отложенного выполнения
|
|
62
|
+
* @private
|
|
63
|
+
*/
|
|
64
|
+
private timer?;
|
|
65
|
+
/**
|
|
66
|
+
* Создать Debouncer с заданной задержкой
|
|
67
|
+
*
|
|
68
|
+
* @param delay Задержка в миллисекундах (0 = без задержки)
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* const debouncer = new Debouncer(300); // 300мс задержка
|
|
73
|
+
* const immediate = new Debouncer(0); // Без задержки
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
constructor(delay: number);
|
|
77
|
+
/**
|
|
78
|
+
* Отложить выполнение функции
|
|
79
|
+
*
|
|
80
|
+
* Если вызывается повторно до истечения delay, предыдущий вызов отменяется
|
|
81
|
+
* и таймер перезапускается.
|
|
82
|
+
*
|
|
83
|
+
* @param fn Функция для выполнения (может быть async)
|
|
84
|
+
* @returns Promise, который разрешается результатом функции
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```typescript
|
|
88
|
+
* const debouncer = new Debouncer(300);
|
|
89
|
+
*
|
|
90
|
+
* // Первый вызов - запланирован через 300мс
|
|
91
|
+
* debouncer.debounce(async () => console.log('First'));
|
|
92
|
+
*
|
|
93
|
+
* // Второй вызов через 100мс - отменяет первый, запланирован через 300мс
|
|
94
|
+
* debouncer.debounce(async () => console.log('Second'));
|
|
95
|
+
*
|
|
96
|
+
* // Через 300мс выведет только: "Second"
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
debounce<T>(fn: () => T | Promise<T>): Promise<T>;
|
|
100
|
+
/**
|
|
101
|
+
* Отменить отложенное выполнение
|
|
102
|
+
*
|
|
103
|
+
* Если есть запланированный вызов, он будет отменен и не выполнится.
|
|
104
|
+
* Promise из debounce() никогда не разрешится.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```typescript
|
|
108
|
+
* const debouncer = new Debouncer(300);
|
|
109
|
+
*
|
|
110
|
+
* debouncer.debounce(async () => {
|
|
111
|
+
* console.log('This will not execute');
|
|
112
|
+
* });
|
|
113
|
+
*
|
|
114
|
+
* debouncer.cancel(); // Отменяем вызов
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
cancel(): void;
|
|
118
|
+
/**
|
|
119
|
+
* Выполнить функцию немедленно, отменив любой отложенный вызов
|
|
120
|
+
*
|
|
121
|
+
* Полезно когда нужно принудительно выполнить действие сейчас,
|
|
122
|
+
* игнорируя debounce.
|
|
123
|
+
*
|
|
124
|
+
* @param fn Функция для немедленного выполнения
|
|
125
|
+
* @returns Promise с результатом функции
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```typescript
|
|
129
|
+
* const debouncer = new Debouncer(300);
|
|
130
|
+
*
|
|
131
|
+
* // Запланировано через 300мс
|
|
132
|
+
* debouncer.debounce(async () => console.log('Delayed'));
|
|
133
|
+
*
|
|
134
|
+
* // Отменяем отложенный и выполняем немедленно
|
|
135
|
+
* await debouncer.flush(async () => console.log('Immediate'));
|
|
136
|
+
* // Выведет: "Immediate" (сразу)
|
|
137
|
+
* // "Delayed" не выполнится (отменен)
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
flush<T>(fn: () => T | Promise<T>): Promise<T>;
|
|
141
|
+
/**
|
|
142
|
+
* Проверить, есть ли активный (запланированный) вызов
|
|
143
|
+
*
|
|
144
|
+
* @returns true если есть запланированный вызов
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* ```typescript
|
|
148
|
+
* const debouncer = new Debouncer(300);
|
|
149
|
+
*
|
|
150
|
+
* console.log(debouncer.isPending()); // false
|
|
151
|
+
*
|
|
152
|
+
* debouncer.debounce(() => console.log('Test'));
|
|
153
|
+
* console.log(debouncer.isPending()); // true
|
|
154
|
+
*
|
|
155
|
+
* // Через 300мс
|
|
156
|
+
* console.log(debouncer.isPending()); // false
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
isPending(): boolean;
|
|
160
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Debouncer - утилита для отложенного выполнения функций
|
|
3
|
+
*
|
|
4
|
+
* Устраняет дублирование debounce логики между field-node.ts и behavior-registry.ts
|
|
5
|
+
*
|
|
6
|
+
* @internal
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const debouncer = new Debouncer(300);
|
|
11
|
+
*
|
|
12
|
+
* // Отложить выполнение на 300мс
|
|
13
|
+
* await debouncer.debounce(async () => {
|
|
14
|
+
* await validateField();
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* // Отменить отложенное выполнение
|
|
18
|
+
* debouncer.cancel();
|
|
19
|
+
*
|
|
20
|
+
* // Выполнить немедленно, отменив отложенное
|
|
21
|
+
* await debouncer.flush(async () => {
|
|
22
|
+
* await validateField();
|
|
23
|
+
* });
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* Класс для debouncing (отложенного выполнения) функций
|
|
28
|
+
*
|
|
29
|
+
* @internal
|
|
30
|
+
*
|
|
31
|
+
* Откладывает выполнение функции на заданное время. Если функция вызывается
|
|
32
|
+
* повторно до истечения времени, предыдущий вызов отменяется.
|
|
33
|
+
*
|
|
34
|
+
* Полезно для:
|
|
35
|
+
* - Отложенной валидации при вводе (debounced validation)
|
|
36
|
+
* - Отложенного сохранения данных
|
|
37
|
+
* - Отложенной обработки событий behaviors
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* // В FieldNode
|
|
42
|
+
* class FieldNode {
|
|
43
|
+
* private validationDebouncer: Debouncer;
|
|
44
|
+
*
|
|
45
|
+
* constructor(config) {
|
|
46
|
+
* this.validationDebouncer = new Debouncer(config.debounce || 0);
|
|
47
|
+
* }
|
|
48
|
+
*
|
|
49
|
+
* async validate(): Promise<boolean> {
|
|
50
|
+
* return this.validationDebouncer.debounce(async () => {
|
|
51
|
+
* // Валидация выполнится через debounce мс
|
|
52
|
+
* return await this.runValidation();
|
|
53
|
+
* });
|
|
54
|
+
* }
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export class Debouncer {
|
|
59
|
+
delay;
|
|
60
|
+
/**
|
|
61
|
+
* Таймер для отложенного выполнения
|
|
62
|
+
* @private
|
|
63
|
+
*/
|
|
64
|
+
timer;
|
|
65
|
+
/**
|
|
66
|
+
* Создать Debouncer с заданной задержкой
|
|
67
|
+
*
|
|
68
|
+
* @param delay Задержка в миллисекундах (0 = без задержки)
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* const debouncer = new Debouncer(300); // 300мс задержка
|
|
73
|
+
* const immediate = new Debouncer(0); // Без задержки
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
constructor(delay) {
|
|
77
|
+
this.delay = delay;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Отложить выполнение функции
|
|
81
|
+
*
|
|
82
|
+
* Если вызывается повторно до истечения delay, предыдущий вызов отменяется
|
|
83
|
+
* и таймер перезапускается.
|
|
84
|
+
*
|
|
85
|
+
* @param fn Функция для выполнения (может быть async)
|
|
86
|
+
* @returns Promise, который разрешается результатом функции
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```typescript
|
|
90
|
+
* const debouncer = new Debouncer(300);
|
|
91
|
+
*
|
|
92
|
+
* // Первый вызов - запланирован через 300мс
|
|
93
|
+
* debouncer.debounce(async () => console.log('First'));
|
|
94
|
+
*
|
|
95
|
+
* // Второй вызов через 100мс - отменяет первый, запланирован через 300мс
|
|
96
|
+
* debouncer.debounce(async () => console.log('Second'));
|
|
97
|
+
*
|
|
98
|
+
* // Через 300мс выведет только: "Second"
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
async debounce(fn) {
|
|
102
|
+
return new Promise((resolve, reject) => {
|
|
103
|
+
// Отменяем предыдущий таймер, если есть
|
|
104
|
+
if (this.timer) {
|
|
105
|
+
clearTimeout(this.timer);
|
|
106
|
+
}
|
|
107
|
+
// Если delay = 0, выполняем немедленно
|
|
108
|
+
if (this.delay === 0) {
|
|
109
|
+
Promise.resolve()
|
|
110
|
+
.then(() => fn())
|
|
111
|
+
.then(resolve)
|
|
112
|
+
.catch(reject);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// Запускаем новый таймер
|
|
116
|
+
this.timer = setTimeout(async () => {
|
|
117
|
+
try {
|
|
118
|
+
const result = await fn();
|
|
119
|
+
resolve(result);
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
reject(error);
|
|
123
|
+
}
|
|
124
|
+
}, this.delay);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Отменить отложенное выполнение
|
|
129
|
+
*
|
|
130
|
+
* Если есть запланированный вызов, он будет отменен и не выполнится.
|
|
131
|
+
* Promise из debounce() никогда не разрешится.
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* ```typescript
|
|
135
|
+
* const debouncer = new Debouncer(300);
|
|
136
|
+
*
|
|
137
|
+
* debouncer.debounce(async () => {
|
|
138
|
+
* console.log('This will not execute');
|
|
139
|
+
* });
|
|
140
|
+
*
|
|
141
|
+
* debouncer.cancel(); // Отменяем вызов
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
cancel() {
|
|
145
|
+
if (this.timer) {
|
|
146
|
+
clearTimeout(this.timer);
|
|
147
|
+
this.timer = undefined;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Выполнить функцию немедленно, отменив любой отложенный вызов
|
|
152
|
+
*
|
|
153
|
+
* Полезно когда нужно принудительно выполнить действие сейчас,
|
|
154
|
+
* игнорируя debounce.
|
|
155
|
+
*
|
|
156
|
+
* @param fn Функция для немедленного выполнения
|
|
157
|
+
* @returns Promise с результатом функции
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* ```typescript
|
|
161
|
+
* const debouncer = new Debouncer(300);
|
|
162
|
+
*
|
|
163
|
+
* // Запланировано через 300мс
|
|
164
|
+
* debouncer.debounce(async () => console.log('Delayed'));
|
|
165
|
+
*
|
|
166
|
+
* // Отменяем отложенный и выполняем немедленно
|
|
167
|
+
* await debouncer.flush(async () => console.log('Immediate'));
|
|
168
|
+
* // Выведет: "Immediate" (сразу)
|
|
169
|
+
* // "Delayed" не выполнится (отменен)
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
async flush(fn) {
|
|
173
|
+
this.cancel();
|
|
174
|
+
return await fn();
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Проверить, есть ли активный (запланированный) вызов
|
|
178
|
+
*
|
|
179
|
+
* @returns true если есть запланированный вызов
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```typescript
|
|
183
|
+
* const debouncer = new Debouncer(300);
|
|
184
|
+
*
|
|
185
|
+
* console.log(debouncer.isPending()); // false
|
|
186
|
+
*
|
|
187
|
+
* debouncer.debounce(() => console.log('Test'));
|
|
188
|
+
* console.log(debouncer.isPending()); // true
|
|
189
|
+
*
|
|
190
|
+
* // Через 300мс
|
|
191
|
+
* console.log(debouncer.isPending()); // false
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
194
|
+
isPending() {
|
|
195
|
+
return this.timer !== undefined;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FormErrorHandler - централизованная обработка ошибок в формах
|
|
3
|
+
*
|
|
4
|
+
* Устраняет несогласованность обработки ошибок между:
|
|
5
|
+
* - field-node.ts (логирует и конвертирует в ValidationError)
|
|
6
|
+
* - behavior-applicator.ts (логирует и пробрасывает)
|
|
7
|
+
* - validation-applicator.ts (логирует и проглатывает)
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* try {
|
|
12
|
+
* await validator(value);
|
|
13
|
+
* } catch (error) {
|
|
14
|
+
* return FormErrorHandler.handle(error, 'AsyncValidator', ErrorStrategy.CONVERT);
|
|
15
|
+
* }
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
import type { ValidationError } from '../types';
|
|
19
|
+
/**
|
|
20
|
+
* Стратегия обработки ошибок
|
|
21
|
+
*
|
|
22
|
+
* Определяет, что делать с ошибкой после логирования
|
|
23
|
+
*/
|
|
24
|
+
export declare enum ErrorStrategy {
|
|
25
|
+
/**
|
|
26
|
+
* Пробросить ошибку дальше (throw)
|
|
27
|
+
* Используется когда ошибка критична и должна остановить выполнение
|
|
28
|
+
*/
|
|
29
|
+
THROW = "throw",
|
|
30
|
+
/**
|
|
31
|
+
* Залогировать и проглотить ошибку (продолжить выполнение)
|
|
32
|
+
* Используется когда ошибка не критична
|
|
33
|
+
*/
|
|
34
|
+
LOG = "log",
|
|
35
|
+
/**
|
|
36
|
+
* Конвертировать ошибку в ValidationError
|
|
37
|
+
* Используется в async validators для отображения ошибки валидации пользователю
|
|
38
|
+
*/
|
|
39
|
+
CONVERT = "convert"
|
|
40
|
+
}
|
|
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 declare 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: unknown, context: string, strategy?: ErrorStrategy): ValidationError | never | void;
|
|
114
|
+
/**
|
|
115
|
+
* Извлечь сообщение из ошибки
|
|
116
|
+
*
|
|
117
|
+
* Обрабатывает различные типы ошибок:
|
|
118
|
+
* - Error объекты → error.message
|
|
119
|
+
* - Строки → возвращает как есть
|
|
120
|
+
* - Объекты с message → извлекает message
|
|
121
|
+
* - Другое → String(error)
|
|
122
|
+
*
|
|
123
|
+
* @param error Ошибка для извлечения сообщения
|
|
124
|
+
* @returns Сообщение ошибки
|
|
125
|
+
* @private
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```typescript
|
|
129
|
+
* FormErrorHandler.extractMessage(new Error('Test'));
|
|
130
|
+
* // 'Test'
|
|
131
|
+
*
|
|
132
|
+
* FormErrorHandler.extractMessage('String error');
|
|
133
|
+
* // 'String error'
|
|
134
|
+
*
|
|
135
|
+
* FormErrorHandler.extractMessage({ message: 'Object error' });
|
|
136
|
+
* // 'Object error'
|
|
137
|
+
*
|
|
138
|
+
* FormErrorHandler.extractMessage(null);
|
|
139
|
+
* // 'null'
|
|
140
|
+
* ```
|
|
141
|
+
*/
|
|
142
|
+
private static extractMessage;
|
|
143
|
+
/**
|
|
144
|
+
* Создать ValidationError с заданными параметрами
|
|
145
|
+
*
|
|
146
|
+
* Утилитная функция для создания ValidationError объектов
|
|
147
|
+
*
|
|
148
|
+
* @param code Код ошибки
|
|
149
|
+
* @param message Сообщение ошибки
|
|
150
|
+
* @param field Поле (опционально)
|
|
151
|
+
* @returns ValidationError объект
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```typescript
|
|
155
|
+
* const error = FormErrorHandler.createValidationError(
|
|
156
|
+
* 'required',
|
|
157
|
+
* 'This field is required',
|
|
158
|
+
* 'email'
|
|
159
|
+
* );
|
|
160
|
+
* // { code: 'required', message: 'This field is required', field: 'email' }
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
|
+
static createValidationError(code: string, message: string, field?: string): ValidationError;
|
|
164
|
+
/**
|
|
165
|
+
* Проверить, является ли объект ValidationError
|
|
166
|
+
*
|
|
167
|
+
* Type guard для ValidationError
|
|
168
|
+
*
|
|
169
|
+
* @param value Значение для проверки
|
|
170
|
+
* @returns true если value является ValidationError
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```typescript
|
|
174
|
+
* if (FormErrorHandler.isValidationError(result)) {
|
|
175
|
+
* console.log(result.code); // OK, типобезопасно
|
|
176
|
+
* }
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
static isValidationError(value: unknown): value is ValidationError;
|
|
180
|
+
}
|