@reformer/core 2.0.0-beta.5 → 2.0.0-beta.7
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-DzYL8kY_.js → behaviors-DyPzh2-X.js} +13 -4
- package/dist/behaviors.js +2 -2
- package/dist/core/nodes/array-node.d.ts +28 -4
- package/dist/core/nodes/field-node.d.ts +12 -6
- package/dist/core/types/form-proxy.d.ts +2 -2
- package/dist/core/types/index.d.ts +35 -1
- package/dist/core/utils/create-form.d.ts +8 -6
- package/dist/core/utils/index.d.ts +1 -0
- package/dist/core/utils/unique-id.d.ts +26 -0
- package/dist/core/validation/validation-context.d.ts +27 -35
- package/dist/core/validation/validation-registry.d.ts +0 -5
- package/dist/index.js +546 -478
- package/dist/{registry-helpers-BRxAr6nG.js → registry-helpers--8-OogF8.js} +12 -25
- package/dist/{validators-gXoHPdqM.js → validators-CWdzevnC.js} +116 -137
- package/dist/validators.js +2 -2
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { b as w, c as E, d as I, B as L } from "./registry-helpers-
|
|
1
|
+
import { b as w, c as E, d as I, B as L } from "./registry-helpers--8-OogF8.js";
|
|
2
2
|
var $ = /* @__PURE__ */ Symbol.for("preact-signals");
|
|
3
3
|
function B() {
|
|
4
4
|
if (y > 1)
|
|
@@ -348,13 +348,22 @@ function Q(e, t, i) {
|
|
|
348
348
|
const m = a.value.value;
|
|
349
349
|
c || h(() => {
|
|
350
350
|
c = !0;
|
|
351
|
-
|
|
352
|
-
|
|
351
|
+
try {
|
|
352
|
+
const U = s ? s(m) : m;
|
|
353
|
+
r.setValue(U, { emitEvent: !1 });
|
|
354
|
+
} finally {
|
|
355
|
+
c = !1;
|
|
356
|
+
}
|
|
353
357
|
});
|
|
354
358
|
}), b = g(() => {
|
|
355
359
|
const m = r.value.value;
|
|
356
360
|
c || h(() => {
|
|
357
|
-
c = !0
|
|
361
|
+
c = !0;
|
|
362
|
+
try {
|
|
363
|
+
a.setValue(m, { emitEvent: !1 });
|
|
364
|
+
} finally {
|
|
365
|
+
c = !1;
|
|
366
|
+
}
|
|
358
367
|
});
|
|
359
368
|
});
|
|
360
369
|
return () => {
|
package/dist/behaviors.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as s, b as r, g as t, c as o, m as l, i as h, f as n, e as i, k as m, j as p, s as c, t as d, l as f, n as F, h as y } from "./behaviors-
|
|
2
|
-
import { d as v, B, c as b } from "./registry-helpers-
|
|
1
|
+
import { a as s, b as r, g as t, c as o, m as l, i as h, f as n, e as i, k as m, j as p, s as c, t as d, l as f, n as F, h as y } from "./behaviors-DyPzh2-X.js";
|
|
2
|
+
import { d as v, B, c as b } from "./registry-helpers--8-OogF8.js";
|
|
3
3
|
export {
|
|
4
4
|
v as BehaviorContextImpl,
|
|
5
5
|
B as BehaviorRegistry,
|
|
@@ -39,6 +39,8 @@ export declare class ArrayNode<T extends FormFields> extends FormNode<T[]> {
|
|
|
39
39
|
* Использует SubscriptionManager вместо массива для управления подписками
|
|
40
40
|
*/
|
|
41
41
|
private disposers;
|
|
42
|
+
/** Array-level validation errors (e.g., "минимум 1 элемент") */
|
|
43
|
+
private readonly _arrayErrors;
|
|
42
44
|
private validationSchemaFn?;
|
|
43
45
|
private behaviorSchemaFn?;
|
|
44
46
|
readonly value: ReadonlySignal<T[]>;
|
|
@@ -74,7 +76,7 @@ export declare class ArrayNode<T extends FormFields> extends FormNode<T[]> {
|
|
|
74
76
|
/**
|
|
75
77
|
* Получить элемент по индексу
|
|
76
78
|
* @param index - Индекс элемента
|
|
77
|
-
* @returns Типизированный GroupNode или undefined если индекс вне границ
|
|
79
|
+
* @returns Типизированный GroupNode proxy или undefined если индекс вне границ
|
|
78
80
|
*/
|
|
79
81
|
at(index: number): FormProxy<T> | undefined;
|
|
80
82
|
getValue(): T[];
|
|
@@ -128,7 +130,29 @@ export declare class ArrayNode<T extends FormFields> extends FormNode<T[]> {
|
|
|
128
130
|
*/
|
|
129
131
|
resetToInitial(): void;
|
|
130
132
|
validate(): Promise<boolean>;
|
|
131
|
-
|
|
133
|
+
/**
|
|
134
|
+
* Установить array-level validation errors
|
|
135
|
+
*
|
|
136
|
+
* @param errors - Массив ошибок валидации уровня массива
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```typescript
|
|
140
|
+
* arrayNode.setErrors([{
|
|
141
|
+
* code: 'minItems',
|
|
142
|
+
* message: 'Минимум 1 элемент обязателен',
|
|
143
|
+
* }]);
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
setErrors(errors: ValidationError[]): void;
|
|
147
|
+
/**
|
|
148
|
+
* Очистить все errors (array-level + item-level)
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```typescript
|
|
152
|
+
* arrayNode.clearErrors();
|
|
153
|
+
* console.log(arrayNode.errors.value); // []
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
132
156
|
clearErrors(): void;
|
|
133
157
|
/**
|
|
134
158
|
* Hook: вызывается после markAsTouched()
|
|
@@ -156,12 +180,12 @@ export declare class ArrayNode<T extends FormFields> extends FormNode<T[]> {
|
|
|
156
180
|
protected onMarkAsPristine(): void;
|
|
157
181
|
/**
|
|
158
182
|
* Итерировать по элементам массива
|
|
159
|
-
* @param callback - Функция, вызываемая для каждого элемента с типизированным GroupNode
|
|
183
|
+
* @param callback - Функция, вызываемая для каждого элемента с типизированным GroupNode proxy
|
|
160
184
|
*/
|
|
161
185
|
forEach(callback: (item: FormProxy<T>, index: number) => void): void;
|
|
162
186
|
/**
|
|
163
187
|
* Маппинг элементов массива
|
|
164
|
-
* @param callback - Функция преобразования с типизированным GroupNode
|
|
188
|
+
* @param callback - Функция преобразования с типизированным GroupNode proxy
|
|
165
189
|
* @returns Новый массив результатов
|
|
166
190
|
*/
|
|
167
191
|
map<R>(callback: (item: FormProxy<T>, index: number) => R): R[];
|
|
@@ -49,6 +49,7 @@ export declare class FieldNode<T> extends FormNode<T> {
|
|
|
49
49
|
private updateOn;
|
|
50
50
|
private initialValue;
|
|
51
51
|
private currentValidationId;
|
|
52
|
+
private currentAbortController?;
|
|
52
53
|
private debounceMs;
|
|
53
54
|
private validateDebounceTimer?;
|
|
54
55
|
private validateDebounceResolve?;
|
|
@@ -137,12 +138,10 @@ export declare class FieldNode<T> extends FormNode<T> {
|
|
|
137
138
|
* Немедленная валидация без debounce
|
|
138
139
|
* @private
|
|
139
140
|
* @remarks
|
|
140
|
-
* Защищена от race conditions:
|
|
141
|
-
* -
|
|
142
|
-
* -
|
|
143
|
-
* -
|
|
144
|
-
* - Проверка перед обработкой async результатов
|
|
145
|
-
* - Проверка перед очисткой errors
|
|
141
|
+
* Защищена от race conditions через AbortController:
|
|
142
|
+
* - Отменяет предыдущую валидацию при запуске новой
|
|
143
|
+
* - Передаёт AbortSignal в async валидаторы для отмены операций (например, fetch)
|
|
144
|
+
* - Проверяет signal.aborted в ключевых точках
|
|
146
145
|
*/
|
|
147
146
|
private validateImmediate;
|
|
148
147
|
setErrors(errors: ValidationError[]): void;
|
|
@@ -256,6 +255,13 @@ export declare class FieldNode<T> extends FormNode<T> {
|
|
|
256
255
|
* Очистить все ресурсы и таймеры
|
|
257
256
|
* Должен вызываться при unmount компонента
|
|
258
257
|
*
|
|
258
|
+
* @remarks
|
|
259
|
+
* Освобождает все ресурсы:
|
|
260
|
+
* - Отписывает все subscriptions через SubscriptionManager
|
|
261
|
+
* - Очищает debounce таймер
|
|
262
|
+
* - Resolve'ит висячий debounce промис (предотвращает утечку памяти)
|
|
263
|
+
* - Отменяет текущую async валидацию через AbortController
|
|
264
|
+
*
|
|
259
265
|
* @example
|
|
260
266
|
* ```typescript
|
|
261
267
|
* useEffect(() => {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* items: Array<{ title: string }>;
|
|
17
17
|
* }
|
|
18
18
|
*
|
|
19
|
-
* const form
|
|
19
|
+
* const form = createForm<MyForm>(schema);
|
|
20
20
|
*
|
|
21
21
|
* // TypeScript знает, что это FieldNode<string>
|
|
22
22
|
* form.name.setValue('John');
|
|
@@ -72,7 +72,7 @@ export type FormControlsProxy<T> = {
|
|
|
72
72
|
* };
|
|
73
73
|
* }
|
|
74
74
|
*
|
|
75
|
-
* const form
|
|
75
|
+
* const form = createForm<UserForm>(schema);
|
|
76
76
|
*
|
|
77
77
|
* // Доступ к методам GroupNode
|
|
78
78
|
* await form.validate();
|
|
@@ -20,12 +20,46 @@ export type UnknownFormValue = unknown;
|
|
|
20
20
|
* @category Validation Types
|
|
21
21
|
*/
|
|
22
22
|
export type ValidatorFn<T = FormValue> = (value: T) => ValidationError | null;
|
|
23
|
+
/**
|
|
24
|
+
* Опции для асинхронного валидатора
|
|
25
|
+
* @group Types
|
|
26
|
+
* @category Validation Types
|
|
27
|
+
*/
|
|
28
|
+
export interface AsyncValidatorOptions {
|
|
29
|
+
/**
|
|
30
|
+
* AbortSignal для отмены валидации
|
|
31
|
+
* Позволяет отменить асинхронную операцию при новой валидации
|
|
32
|
+
*/
|
|
33
|
+
signal?: AbortSignal;
|
|
34
|
+
}
|
|
23
35
|
/**
|
|
24
36
|
* Асинхронная функция валидации
|
|
37
|
+
*
|
|
38
|
+
* @param value - Значение для валидации
|
|
39
|
+
* @param options - Опции валидации (опционально)
|
|
40
|
+
* @returns Promise с ошибкой валидации или null если значение валидно
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* // Простой валидатор (без поддержки отмены)
|
|
45
|
+
* const emailExists: AsyncValidatorFn<string> = async (value) => {
|
|
46
|
+
* const exists = await checkEmail(value);
|
|
47
|
+
* return exists ? { code: 'exists', message: 'Email already exists' } : null;
|
|
48
|
+
* };
|
|
49
|
+
*
|
|
50
|
+
* // Валидатор с поддержкой отмены
|
|
51
|
+
* const emailExistsAbortable: AsyncValidatorFn<string> = async (value, options) => {
|
|
52
|
+
* const exists = await fetch(`/api/check-email?email=${value}`, {
|
|
53
|
+
* signal: options?.signal // Передаём signal в fetch для отмены запроса
|
|
54
|
+
* });
|
|
55
|
+
* return exists ? { code: 'exists', message: 'Email already exists' } : null;
|
|
56
|
+
* };
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
25
59
|
* @group Types
|
|
26
60
|
* @category Validation Types
|
|
27
61
|
*/
|
|
28
|
-
export type AsyncValidatorFn<T = FormValue> = (value: T) => Promise<ValidationError | null>;
|
|
62
|
+
export type AsyncValidatorFn<T = FormValue> = (value: T, options?: AsyncValidatorOptions) => Promise<ValidationError | null>;
|
|
29
63
|
/**
|
|
30
64
|
* Ошибка валидации
|
|
31
65
|
* @group Types
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Фабричная функция для создания формы с правильной типизацией
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Это рекомендуемый способ создания форм в ReFormer v2.0+.
|
|
5
|
+
* Создаёт GroupNode и возвращает его Proxy для типобезопасного доступа к полям.
|
|
6
6
|
*
|
|
7
7
|
* @group Utilities
|
|
8
8
|
*
|
|
9
9
|
* @example
|
|
10
10
|
* ```typescript
|
|
11
|
-
* //
|
|
12
|
-
* const form: FormProxy<MyForm> = new GroupNode<MyForm>(config);
|
|
13
|
-
*
|
|
14
|
-
* // Используйте:
|
|
11
|
+
* // Рекомендуемый способ:
|
|
15
12
|
* const form = createForm<MyForm>(config);
|
|
13
|
+
* form.email.setValue('test@mail.com'); // Типобезопасный доступ к полям
|
|
14
|
+
*
|
|
15
|
+
* // Если нужен именно GroupNode instance:
|
|
16
|
+
* const groupNode = new GroupNode<MyForm>(config);
|
|
17
|
+
* const proxy = groupNode.getProxy(); // Явно получаем Proxy
|
|
16
18
|
* ```
|
|
17
19
|
*/
|
|
18
20
|
import type { FormProxy, GroupNodeConfig, FormSchema } from '../types';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Генератор уникальных идентификаторов
|
|
3
|
+
*
|
|
4
|
+
* Использует атомный счётчик для генерации гарантированно уникальных ключей.
|
|
5
|
+
* Решает проблему возможного дублирования ключей при использовании
|
|
6
|
+
* Date.now() + Math.random() в быстрых последовательных вызовах.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const key1 = uniqueId('watch'); // "watch-1"
|
|
11
|
+
* const key2 = uniqueId('watch'); // "watch-2"
|
|
12
|
+
* const key3 = uniqueId('effect'); // "effect-3"
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Генерирует уникальный идентификатор с указанным префиксом
|
|
17
|
+
*
|
|
18
|
+
* @param prefix - Префикс для идентификатора
|
|
19
|
+
* @returns Уникальный идентификатор в формате `${prefix}-${counter}`
|
|
20
|
+
*/
|
|
21
|
+
export declare function uniqueId(prefix: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Сбросить счётчик (только для тестов)
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
export declare function resetUniqueIdCounter(): void;
|
|
@@ -1,27 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Реализация контекста валидации
|
|
3
|
+
*
|
|
4
|
+
* Использует паттерн наследования для устранения дублирования кода
|
|
5
|
+
* между различными типами контекстов валидации.
|
|
3
6
|
*/
|
|
4
7
|
import type { GroupNode } from '../nodes/group-node';
|
|
5
8
|
import type { FieldNode } from '../nodes/field-node';
|
|
6
9
|
import type { FormProxy } from '../types/form-proxy';
|
|
7
10
|
import type { FormContext } from '../types/form-context';
|
|
8
11
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
12
|
+
* Базовый класс контекста валидации
|
|
13
|
+
* Содержит общую логику для всех типов контекстов
|
|
14
|
+
* @internal
|
|
11
15
|
*/
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
private control;
|
|
16
|
+
declare abstract class BaseValidationContext<TForm> implements FormContext<TForm> {
|
|
17
|
+
protected readonly _form: GroupNode<TForm>;
|
|
15
18
|
/**
|
|
16
19
|
* Форма с типизированным Proxy-доступом к полям
|
|
17
20
|
*/
|
|
18
21
|
readonly form: FormProxy<TForm>;
|
|
19
|
-
constructor(form: GroupNode<TForm
|
|
20
|
-
/**
|
|
21
|
-
* Получить текущее значение поля (внутренний метод для validation-applicator)
|
|
22
|
-
* @internal
|
|
23
|
-
*/
|
|
24
|
-
value(): TField;
|
|
22
|
+
constructor(form: GroupNode<TForm>);
|
|
25
23
|
/**
|
|
26
24
|
* Безопасно установить значение поля по строковому пути
|
|
27
25
|
* Автоматически использует emitEvent: false для предотвращения циклов
|
|
@@ -29,42 +27,36 @@ export declare class ValidationContextImpl<TForm, TField> implements FormContext
|
|
|
29
27
|
setFieldValue(path: string, value: unknown): void;
|
|
30
28
|
}
|
|
31
29
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
30
|
+
* Контекст валидации для отдельного поля
|
|
31
|
+
* Предоставляет доступ к значению конкретного поля
|
|
34
32
|
*/
|
|
35
|
-
export declare class
|
|
36
|
-
private
|
|
33
|
+
export declare class ValidationContextImpl<TForm, TField> extends BaseValidationContext<TForm> {
|
|
34
|
+
private control;
|
|
35
|
+
constructor(form: GroupNode<TForm>, _fieldKey: keyof TForm, control: FieldNode<TField>);
|
|
37
36
|
/**
|
|
38
|
-
*
|
|
37
|
+
* Получить текущее значение поля (внутренний метод для validation-applicator)
|
|
38
|
+
* @internal
|
|
39
39
|
*/
|
|
40
|
-
|
|
40
|
+
value(): TField;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Контекст для cross-field валидации
|
|
44
|
+
* Не предоставляет доступ к конкретному полю, только к форме целиком
|
|
45
|
+
*/
|
|
46
|
+
export declare class TreeValidationContextImpl<TForm> extends BaseValidationContext<TForm> {
|
|
41
47
|
constructor(form: GroupNode<TForm>);
|
|
42
|
-
/**
|
|
43
|
-
* Безопасно установить значение поля по строковому пути
|
|
44
|
-
* Автоматически использует emitEvent: false для предотвращения циклов
|
|
45
|
-
*/
|
|
46
|
-
setFieldValue(path: string, value: unknown): void;
|
|
47
48
|
}
|
|
48
49
|
/**
|
|
49
|
-
*
|
|
50
|
-
*
|
|
50
|
+
* Контекст валидации для ArrayNode
|
|
51
|
+
* Предоставляет доступ к значению массива
|
|
51
52
|
*/
|
|
52
|
-
export declare class ArrayValidationContextImpl<TForm, TItem>
|
|
53
|
-
private _form;
|
|
53
|
+
export declare class ArrayValidationContextImpl<TForm, TItem> extends BaseValidationContext<TForm> {
|
|
54
54
|
private arrayValue;
|
|
55
|
-
/**
|
|
56
|
-
* Форма с типизированным Proxy-доступом к полям
|
|
57
|
-
*/
|
|
58
|
-
readonly form: FormProxy<TForm>;
|
|
59
55
|
constructor(form: GroupNode<TForm>, _fieldKey: keyof TForm, arrayValue: TItem[]);
|
|
60
56
|
/**
|
|
61
57
|
* Получить текущее значение массива (для валидатора)
|
|
62
58
|
* @internal
|
|
63
59
|
*/
|
|
64
60
|
value(): TItem[];
|
|
65
|
-
/**
|
|
66
|
-
* Безопасно установить значение поля по строковому пути
|
|
67
|
-
* Автоматически использует emitEvent: false для предотвращения циклов
|
|
68
|
-
*/
|
|
69
|
-
setFieldValue(path: string, value: unknown): void;
|
|
70
61
|
}
|
|
62
|
+
export {};
|
|
@@ -142,11 +142,6 @@ export declare class ValidationRegistry {
|
|
|
142
142
|
* Возвращает локальный массив валидаторов (без аргумента form).
|
|
143
143
|
*/
|
|
144
144
|
getValidators(): ValidatorRegistration[];
|
|
145
|
-
/**
|
|
146
|
-
* Применить зарегистрированные валидаторы к GroupNode
|
|
147
|
-
* @private
|
|
148
|
-
*/
|
|
149
|
-
private applyValidators;
|
|
150
145
|
/**
|
|
151
146
|
* Применить array-items validators к ArrayNode элементам
|
|
152
147
|
* @private
|