@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,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Типы для схемы формы (Deep Schema)
|
|
3
|
+
*
|
|
4
|
+
* Автоматическое определение типов схемы на основе структуры данных:
|
|
5
|
+
* - `[{...}]` → массив форм
|
|
6
|
+
* - `{...}` → вложенная группа
|
|
7
|
+
* - `{value, component}` → конфигурация поля
|
|
8
|
+
*
|
|
9
|
+
* @group Types
|
|
10
|
+
*/
|
|
11
|
+
import type { ComponentType } from 'react';
|
|
12
|
+
import type { ValidatorFn, AsyncValidatorFn, FormFields, AnyFunction } from './index';
|
|
13
|
+
/**
|
|
14
|
+
* Конфигурация поля
|
|
15
|
+
* @group Types
|
|
16
|
+
* @category Configuration Types
|
|
17
|
+
*/
|
|
18
|
+
export interface FieldConfig<T> {
|
|
19
|
+
value: T | null;
|
|
20
|
+
component: ComponentType<any>;
|
|
21
|
+
componentProps?: any;
|
|
22
|
+
validators?: ValidatorFn<T>[];
|
|
23
|
+
asyncValidators?: AsyncValidatorFn<T>[];
|
|
24
|
+
disabled?: boolean;
|
|
25
|
+
updateOn?: 'change' | 'blur' | 'submit';
|
|
26
|
+
/** Задержка (в мс) перед запуском асинхронной валидации */
|
|
27
|
+
debounce?: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Конфигурация массива
|
|
31
|
+
* @group Types
|
|
32
|
+
* @category Configuration Types
|
|
33
|
+
*/
|
|
34
|
+
export interface ArrayConfig<T extends FormFields> {
|
|
35
|
+
itemSchema: FormSchema<T>;
|
|
36
|
+
initial?: Partial<T>[];
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Автоматически определяет тип схемы на основе TypeScript типа:
|
|
40
|
+
* - `T[] -> [FormSchema<T>]` (массив с одним элементом)
|
|
41
|
+
* - `object -> FormSchema<T>` (группа)
|
|
42
|
+
* - `primitive -> FieldConfig<T>` (поле)
|
|
43
|
+
*
|
|
44
|
+
* Использует NonNullable для корректной обработки опциональных полей
|
|
45
|
+
*
|
|
46
|
+
* @group Types
|
|
47
|
+
* @category Configuration Types
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* interface Form {
|
|
52
|
+
* name: string; // → FieldConfig<string>
|
|
53
|
+
* address: { // → FormSchema<Address>
|
|
54
|
+
* city: string;
|
|
55
|
+
* street: string;
|
|
56
|
+
* };
|
|
57
|
+
* items?: Array<{ // → [FormSchema<Item>] (опциональный)
|
|
58
|
+
* title: string;
|
|
59
|
+
* price: number;
|
|
60
|
+
* }>;
|
|
61
|
+
* }
|
|
62
|
+
*
|
|
63
|
+
* const schema: FormSchema<Form> = {
|
|
64
|
+
* name: { value: '', component: Input },
|
|
65
|
+
* address: {
|
|
66
|
+
* city: { value: '', component: Input },
|
|
67
|
+
* street: { value: '', component: Input },
|
|
68
|
+
* },
|
|
69
|
+
* items: [{
|
|
70
|
+
* title: { value: '', component: Input },
|
|
71
|
+
* price: { value: 0, component: Input },
|
|
72
|
+
* }],
|
|
73
|
+
* };
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export type FormSchema<T> = {
|
|
77
|
+
[K in keyof T]: NonNullable<T[K]> extends string | number | boolean ? FieldConfig<T[K]> : NonNullable<T[K]> extends Array<infer U> ? U extends string | number | boolean ? FieldConfig<T[K]> : U extends Date | File | Blob | AnyFunction ? FieldConfig<T[K]> : [FormSchema<U>] : NonNullable<T[K]> extends Date | File | Blob | AnyFunction ? FieldConfig<T[K]> : FormSchema<NonNullable<T[K]>>;
|
|
78
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Типы для схемы формы (Deep Schema)
|
|
3
|
+
*
|
|
4
|
+
* Автоматическое определение типов схемы на основе структуры данных:
|
|
5
|
+
* - `[{...}]` → массив форм
|
|
6
|
+
* - `{...}` → вложенная группа
|
|
7
|
+
* - `{value, component}` → конфигурация поля
|
|
8
|
+
*
|
|
9
|
+
* @group Types
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { AnyFunction } from './index';
|
|
2
|
+
/**
|
|
3
|
+
* FieldPath предоставляет типобезопасный доступ к путям полей формы
|
|
4
|
+
*
|
|
5
|
+
* Рекурсивно обрабатывает вложенные объекты для поддержки вложенных форм.
|
|
6
|
+
*
|
|
7
|
+
* Использование:
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const validation = (path: FieldPath<MyForm>) => {
|
|
10
|
+
* required(path.email, { message: 'Email обязателен' });
|
|
11
|
+
*
|
|
12
|
+
* // Вложенные объекты
|
|
13
|
+
* required(path.registrationAddress.city);
|
|
14
|
+
* minLength(path.registrationAddress.street, 3);
|
|
15
|
+
*
|
|
16
|
+
* applyWhen(
|
|
17
|
+
* path.loanType,
|
|
18
|
+
* (type) => type === 'mortgage',
|
|
19
|
+
* (path) => {
|
|
20
|
+
* required(path.propertyValue, { message: 'Укажите стоимость' });
|
|
21
|
+
* }
|
|
22
|
+
* );
|
|
23
|
+
* };
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export type FieldPath<T> = {
|
|
27
|
+
[K in keyof T]: NonNullable<T[K]> extends Array<unknown> ? FieldPathNode<T, T[K], K> : NonNullable<T[K]> extends Date | File | Blob | AnyFunction ? FieldPathNode<T, T[K], K> : NonNullable<T[K]> extends object ? FieldPathNode<T, T[K], K> & FieldPath<NonNullable<T[K]>> : FieldPathNode<T, T[K], K>;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Узел в пути поля
|
|
31
|
+
* Содержит метаинформацию о поле для валидации
|
|
32
|
+
*/
|
|
33
|
+
export interface FieldPathNode<TForm, TField, TKey = unknown> {
|
|
34
|
+
/** Ключ поля */
|
|
35
|
+
readonly __key: TKey;
|
|
36
|
+
/** Путь к полю (для вложенных объектов) */
|
|
37
|
+
readonly __path: string;
|
|
38
|
+
/** Тип формы */
|
|
39
|
+
readonly __formType?: TForm;
|
|
40
|
+
/** Тип поля */
|
|
41
|
+
readonly __fieldType?: TField;
|
|
42
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Единый контекст для работы с формой
|
|
3
|
+
*
|
|
4
|
+
* Используется в:
|
|
5
|
+
* - Behavior схемах (watchField, copyFrom, transformValue, etc.)
|
|
6
|
+
* - Validation схемах (validate, validateAsync, validateTree)
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* // Behavior
|
|
11
|
+
* watchField(path.country, (country, ctx) => {
|
|
12
|
+
* ctx.form.city.updateComponentProps({ options: cities });
|
|
13
|
+
* ctx.setFieldValue('city', null);
|
|
14
|
+
* });
|
|
15
|
+
*
|
|
16
|
+
* // Validation
|
|
17
|
+
* validate(path.email, (value, ctx) => {
|
|
18
|
+
* if (!value) return { code: 'required', message: 'Required' };
|
|
19
|
+
* const confirm = ctx.form.confirmEmail.value.value;
|
|
20
|
+
* if (value !== confirm) return { code: 'mismatch', message: 'Emails must match' };
|
|
21
|
+
* return null;
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
import type { GroupNodeWithControls } from './group-node-proxy';
|
|
26
|
+
/**
|
|
27
|
+
* Единый контекст для работы с формой
|
|
28
|
+
*
|
|
29
|
+
* Предоставляет:
|
|
30
|
+
* - `form` - типизированный Proxy-доступ к полям формы
|
|
31
|
+
* - `setFieldValue` - безопасная установка значения (emitEvent: false)
|
|
32
|
+
*/
|
|
33
|
+
export interface FormContext<TForm> {
|
|
34
|
+
/**
|
|
35
|
+
* Форма с типизированным Proxy-доступом к полям
|
|
36
|
+
*
|
|
37
|
+
* Позволяет обращаться к полям напрямую через точечную нотацию:
|
|
38
|
+
* - `ctx.form.email` → FieldNode
|
|
39
|
+
* - `ctx.form.address.city` → FieldNode (вложенный)
|
|
40
|
+
* - `ctx.form.items` → ArrayNode
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* // Получить значение
|
|
45
|
+
* ctx.form.email.value.value
|
|
46
|
+
*
|
|
47
|
+
* // Установить значение (⚠️ может вызвать цикл в behavior!)
|
|
48
|
+
* ctx.form.email.setValue('new@mail.com')
|
|
49
|
+
*
|
|
50
|
+
* // Безопасно установить значение
|
|
51
|
+
* ctx.form.email.setValue('new@mail.com', { emitEvent: false })
|
|
52
|
+
*
|
|
53
|
+
* // Обновить пропсы компонента
|
|
54
|
+
* ctx.form.city.updateComponentProps({ options: cities })
|
|
55
|
+
*
|
|
56
|
+
* // Валидация поля
|
|
57
|
+
* await ctx.form.email.validate()
|
|
58
|
+
*
|
|
59
|
+
* // Работа с массивами
|
|
60
|
+
* ctx.form.items.push({ title: 'New' })
|
|
61
|
+
* ctx.form.items.clear()
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
readonly form: GroupNodeWithControls<TForm>;
|
|
65
|
+
/**
|
|
66
|
+
* Безопасно установить значение поля по строковому пути
|
|
67
|
+
*
|
|
68
|
+
* Автоматически использует `emitEvent: false` для предотвращения
|
|
69
|
+
* бесконечных циклов в behavior схемах.
|
|
70
|
+
*
|
|
71
|
+
* @param path - Путь к полю (например, "address.city", "items[0].name")
|
|
72
|
+
* @param value - Новое значение
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```typescript
|
|
76
|
+
* // Сброс города при смене страны
|
|
77
|
+
* watchField(path.country, (country, ctx) => {
|
|
78
|
+
* ctx.setFieldValue('city', null);
|
|
79
|
+
* });
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
setFieldValue(path: string, value: unknown): void;
|
|
83
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Единый контекст для работы с формой
|
|
3
|
+
*
|
|
4
|
+
* Используется в:
|
|
5
|
+
* - Behavior схемах (watchField, copyFrom, transformValue, etc.)
|
|
6
|
+
* - Validation схемах (validate, validateAsync, validateTree)
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* // Behavior
|
|
11
|
+
* watchField(path.country, (country, ctx) => {
|
|
12
|
+
* ctx.form.city.updateComponentProps({ options: cities });
|
|
13
|
+
* ctx.setFieldValue('city', null);
|
|
14
|
+
* });
|
|
15
|
+
*
|
|
16
|
+
* // Validation
|
|
17
|
+
* validate(path.email, (value, ctx) => {
|
|
18
|
+
* if (!value) return { code: 'required', message: 'Required' };
|
|
19
|
+
* const confirm = ctx.form.confirmEmail.value.value;
|
|
20
|
+
* if (value !== confirm) return { code: 'mismatch', message: 'Emails must match' };
|
|
21
|
+
* return null;
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Типы для Proxy-доступа к полям GroupNode
|
|
3
|
+
*
|
|
4
|
+
* Решает проблему типизации, когда GroupNode<T> использует Proxy для прямого доступа к полям.
|
|
5
|
+
* TypeScript не может автоматически определить правильные типы для вложенных форм и массивов.
|
|
6
|
+
*
|
|
7
|
+
* @group Types
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* interface MyForm {
|
|
12
|
+
* name: string;
|
|
13
|
+
* address: {
|
|
14
|
+
* city: string;
|
|
15
|
+
* };
|
|
16
|
+
* items: Array<{ title: string }>;
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* const form: GroupNodeWithControls<MyForm> = new GroupNode(schema);
|
|
20
|
+
*
|
|
21
|
+
* // TypeScript знает, что это FieldNode<string>
|
|
22
|
+
* form.name.setValue('John');
|
|
23
|
+
*
|
|
24
|
+
* // TypeScript знает, что это GroupNode<{city: string}>
|
|
25
|
+
* form.address.city.setValue('Moscow');
|
|
26
|
+
*
|
|
27
|
+
* // TypeScript знает, что это ArrayNode<{title: string}>
|
|
28
|
+
* form.items.push({ title: 'New Item' });
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
import type { FormFields } from './index';
|
|
32
|
+
type FieldNode<_T> = any;
|
|
33
|
+
type GroupNode<_T> = any;
|
|
34
|
+
type ArrayNode<_T> = any;
|
|
35
|
+
/**
|
|
36
|
+
* Мапит тип модели данных T на правильные типы узлов формы
|
|
37
|
+
*
|
|
38
|
+
* Рекурсивно определяет типы узлов на основе структуры данных:
|
|
39
|
+
* - `T[K] extends Array<infer U>` где U - объект → `ArrayNodeWithControls<U>`
|
|
40
|
+
* - `T[K] extends Array<infer U>` где U - примитив → `FieldNode<T[K]>` (массив как обычное поле)
|
|
41
|
+
* - `T[K] extends object` → `GroupNodeWithControls<T[K]>` (вложенная форма с типизацией)
|
|
42
|
+
* - `T[K]` примитив → `FieldNode<T[K]>` (простое поле)
|
|
43
|
+
*
|
|
44
|
+
* Использует NonNullable для правильной обработки опциональных полей
|
|
45
|
+
*
|
|
46
|
+
* @group Types
|
|
47
|
+
* @category Proxy Types
|
|
48
|
+
*
|
|
49
|
+
* @template T - Тип модели данных формы
|
|
50
|
+
*/
|
|
51
|
+
export type FormNodeControls<T> = {
|
|
52
|
+
[K in keyof T]: NonNullable<T[K]> extends Array<infer U> ? U extends FormFields ? ArrayNodeWithControls<U> : FieldNode<T[K]> : NonNullable<T[K]> extends FormFields ? NonNullable<T[K]> extends Date | File | Blob ? FieldNode<T[K]> : GroupNodeWithControls<NonNullable<T[K]>> : FieldNode<T[K]>;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Комбинированный тип для GroupNode с Proxy доступом к полям
|
|
56
|
+
*
|
|
57
|
+
* Объединяет методы и свойства GroupNode с типизированными полями формы.
|
|
58
|
+
* Это позволяет использовать как API GroupNode, так и прямой доступ к полям.
|
|
59
|
+
*
|
|
60
|
+
* @group Types
|
|
61
|
+
* @category Proxy Types
|
|
62
|
+
*
|
|
63
|
+
* @template T - Тип модели данных формы
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```typescript
|
|
67
|
+
* interface UserForm {
|
|
68
|
+
* email: string;
|
|
69
|
+
* profile: {
|
|
70
|
+
* name: string;
|
|
71
|
+
* age: number;
|
|
72
|
+
* };
|
|
73
|
+
* }
|
|
74
|
+
*
|
|
75
|
+
* const form: GroupNodeWithControls<UserForm> = new GroupNode(schema);
|
|
76
|
+
*
|
|
77
|
+
* // Доступ к методам GroupNode
|
|
78
|
+
* await form.validate();
|
|
79
|
+
* const values = form.getValue();
|
|
80
|
+
* console.log(form.valid.value);
|
|
81
|
+
*
|
|
82
|
+
* // Прямой доступ к полям (через Proxy)
|
|
83
|
+
* form.email.setValue('test@mail.com');
|
|
84
|
+
* form.profile.name.setValue('John');
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
export type GroupNodeWithControls<T> = GroupNode<T> & FormNodeControls<T>;
|
|
88
|
+
/**
|
|
89
|
+
* Комбинированный тип для ArrayNode с Proxy доступом к элементам
|
|
90
|
+
*
|
|
91
|
+
* Объединяет методы и свойства ArrayNode с типизированным доступом к элементам массива.
|
|
92
|
+
*
|
|
93
|
+
* @group Types
|
|
94
|
+
* @category Proxy Types
|
|
95
|
+
*
|
|
96
|
+
* @template T - Тип модели данных элемента массива
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```typescript
|
|
100
|
+
* interface TodoItem {
|
|
101
|
+
* title: string;
|
|
102
|
+
* completed: boolean;
|
|
103
|
+
* }
|
|
104
|
+
*
|
|
105
|
+
* const todos: ArrayNodeWithControls<TodoItem> = new ArrayNode(schema);
|
|
106
|
+
*
|
|
107
|
+
* // Доступ к методам ArrayNode
|
|
108
|
+
* todos.push({ title: 'New todo', completed: false });
|
|
109
|
+
* todos.removeAt(0);
|
|
110
|
+
*
|
|
111
|
+
* // Доступ к элементам (через Proxy)
|
|
112
|
+
* todos.at(0)?.title.setValue('Updated title');
|
|
113
|
+
*
|
|
114
|
+
* // Итерация
|
|
115
|
+
* todos.forEach((item, i) => {
|
|
116
|
+
* console.log(item.title.value.value);
|
|
117
|
+
* });
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
export type ArrayNodeWithControls<T extends FormFields> = ArrayNode<T> & {
|
|
121
|
+
/**
|
|
122
|
+
* Безопасный доступ к элементу массива по индексу
|
|
123
|
+
* Возвращает GroupNode с типизированными полями или undefined
|
|
124
|
+
*/
|
|
125
|
+
at(index: number): GroupNodeWithControls<T> | undefined;
|
|
126
|
+
/**
|
|
127
|
+
* Итерация по элементам массива с типизированными элементами
|
|
128
|
+
*/
|
|
129
|
+
forEach(callback: (item: GroupNodeWithControls<T>, index: number) => void): void;
|
|
130
|
+
/**
|
|
131
|
+
* Маппинг элементов массива с типизированными элементами
|
|
132
|
+
*/
|
|
133
|
+
map<R>(callback: (item: GroupNodeWithControls<T>, index: number) => R): R[];
|
|
134
|
+
};
|
|
135
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Типы для Proxy-доступа к полям GroupNode
|
|
3
|
+
*
|
|
4
|
+
* Решает проблему типизации, когда GroupNode<T> использует Proxy для прямого доступа к полям.
|
|
5
|
+
* TypeScript не может автоматически определить правильные типы для вложенных форм и массивов.
|
|
6
|
+
*
|
|
7
|
+
* @group Types
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* interface MyForm {
|
|
12
|
+
* name: string;
|
|
13
|
+
* address: {
|
|
14
|
+
* city: string;
|
|
15
|
+
* };
|
|
16
|
+
* items: Array<{ title: string }>;
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* const form: GroupNodeWithControls<MyForm> = new GroupNode(schema);
|
|
20
|
+
*
|
|
21
|
+
* // TypeScript знает, что это FieldNode<string>
|
|
22
|
+
* form.name.setValue('John');
|
|
23
|
+
*
|
|
24
|
+
* // TypeScript знает, что это GroupNode<{city: string}>
|
|
25
|
+
* form.address.city.setValue('Moscow');
|
|
26
|
+
*
|
|
27
|
+
* // TypeScript знает, что это ArrayNode<{title: string}>
|
|
28
|
+
* form.items.push({ title: 'New Item' });
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents any valid form value type
|
|
3
|
+
* Use this instead of 'any' for form values to maintain type safety
|
|
4
|
+
*
|
|
5
|
+
* @group Types
|
|
6
|
+
* @category Core Types
|
|
7
|
+
*/
|
|
8
|
+
export type FormValue = string | number | boolean | null | undefined | Date | File | FormValue[] | {
|
|
9
|
+
[key: string]: FormValue;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Type-safe alternative to 'any' for unknown form values
|
|
13
|
+
* Requires explicit type checking before use
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
export type UnknownFormValue = unknown;
|
|
17
|
+
/**
|
|
18
|
+
* Синхронная функция валидации
|
|
19
|
+
* @group Types
|
|
20
|
+
* @category Validation Types
|
|
21
|
+
*/
|
|
22
|
+
export type ValidatorFn<T = FormValue> = (value: T) => ValidationError | null;
|
|
23
|
+
/**
|
|
24
|
+
* Асинхронная функция валидации
|
|
25
|
+
* @group Types
|
|
26
|
+
* @category Validation Types
|
|
27
|
+
*/
|
|
28
|
+
export type AsyncValidatorFn<T = FormValue> = (value: T) => Promise<ValidationError | null>;
|
|
29
|
+
/**
|
|
30
|
+
* Ошибка валидации
|
|
31
|
+
* @group Types
|
|
32
|
+
* @category Validation Types
|
|
33
|
+
*/
|
|
34
|
+
export interface ValidationError {
|
|
35
|
+
code: string;
|
|
36
|
+
message: string;
|
|
37
|
+
params?: FormFields;
|
|
38
|
+
/** Severity level: 'error' (default) blocks submission, 'warning' shows message but allows submission */
|
|
39
|
+
severity?: 'error' | 'warning';
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Опции для фильтрации ошибок в методе getErrors()
|
|
43
|
+
* @group Types
|
|
44
|
+
* @category Validation Types
|
|
45
|
+
*/
|
|
46
|
+
export interface ErrorFilterOptions {
|
|
47
|
+
/** Фильтр по коду ошибки */
|
|
48
|
+
code?: string | string[];
|
|
49
|
+
/** Фильтр по сообщению (поддерживает частичное совпадение) */
|
|
50
|
+
message?: string;
|
|
51
|
+
/** Фильтр по параметрам ошибки */
|
|
52
|
+
params?: FormFields;
|
|
53
|
+
/** Кастомный предикат для фильтрации */
|
|
54
|
+
predicate?: (error: ValidationError) => boolean;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Статус поля формы
|
|
58
|
+
* @group Types
|
|
59
|
+
* @category Core Types
|
|
60
|
+
*/
|
|
61
|
+
export type FieldStatus = 'valid' | 'invalid' | 'pending' | 'disabled';
|
|
62
|
+
export type { FieldConfig } from './deep-schema';
|
|
63
|
+
export type { FieldPath, FieldPathNode } from '../types/field-path';
|
|
64
|
+
export type { ContextualValidatorFn, ContextualAsyncValidatorFn, TreeValidatorFn, ConditionFn, ValidateOptions, ValidateAsyncOptions, ValidateTreeOptions, ValidationSchemaFn, ValidatorRegistration, } from './validation-schema';
|
|
65
|
+
export type { FormSchema, ArrayConfig } from './deep-schema';
|
|
66
|
+
export type { FormContext } from './form-context';
|
|
67
|
+
export type { FormNodeControls, GroupNodeWithControls, ArrayNodeWithControls, } from './group-node-proxy';
|
|
68
|
+
import type { BehaviorSchemaFn } from '../behavior/types';
|
|
69
|
+
import type { FormSchema } from './deep-schema';
|
|
70
|
+
import type { ValidationSchemaFn } from './validation-schema';
|
|
71
|
+
/**
|
|
72
|
+
* Конфигурация GroupNode с поддержкой схем
|
|
73
|
+
* Используется для создания форм с автоматическим применением behavior и validation схем
|
|
74
|
+
*
|
|
75
|
+
* @group Types
|
|
76
|
+
* @category Configuration Types
|
|
77
|
+
*/
|
|
78
|
+
export interface GroupNodeConfig<T> {
|
|
79
|
+
/** Схема структуры формы (поля и их конфигурация) */
|
|
80
|
+
form: FormSchema<T>;
|
|
81
|
+
/** Схема реактивного поведения (copyFrom, enableWhen, computeFrom и т.д.) */
|
|
82
|
+
behavior?: BehaviorSchemaFn<T>;
|
|
83
|
+
/** Схема валидации (required, email, minLength и т.д.) */
|
|
84
|
+
validation?: ValidationSchemaFn<T>;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Тип для Record с unknown значениями
|
|
88
|
+
* Используется вместо инлайнового `Record<string, unknown>`
|
|
89
|
+
* @internal
|
|
90
|
+
*/
|
|
91
|
+
export type UnknownRecord = Record<string, any>;
|
|
92
|
+
/**
|
|
93
|
+
* Интерфейс для узлов с методом applyValidationSchema
|
|
94
|
+
* @internal
|
|
95
|
+
*/
|
|
96
|
+
export interface WithValidationSchema {
|
|
97
|
+
applyValidationSchema(schemaFn: unknown): void;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Интерфейс для узлов с методом applyBehaviorSchema
|
|
101
|
+
* @internal
|
|
102
|
+
*/
|
|
103
|
+
export interface WithBehaviorSchema {
|
|
104
|
+
applyBehaviorSchema(schemaFn: unknown): void;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Интерфейс для узлов, похожих на ArrayNode (с методом at)
|
|
108
|
+
* Используется для duck typing при обходе путей
|
|
109
|
+
* @internal
|
|
110
|
+
*/
|
|
111
|
+
export interface ArrayNodeLike {
|
|
112
|
+
at(index: number): FormNode<unknown> | undefined;
|
|
113
|
+
length: unknown;
|
|
114
|
+
}
|
|
115
|
+
import type { FormNode } from '../nodes/form-node';
|
|
116
|
+
/**
|
|
117
|
+
* Конфиг с полем schema (для ArrayConfig)
|
|
118
|
+
* @internal
|
|
119
|
+
*/
|
|
120
|
+
export interface ConfigWithSchema {
|
|
121
|
+
schema: unknown;
|
|
122
|
+
initialItems?: unknown[];
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Конфиг с полем value (для извлечения значений)
|
|
126
|
+
* @internal
|
|
127
|
+
*/
|
|
128
|
+
export interface ConfigWithValue {
|
|
129
|
+
value: unknown;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Тип для конфига с полями (FormSchema generic constraint)
|
|
133
|
+
* Используется вместо `Record<string, any>` для схем форм
|
|
134
|
+
* @deprecated
|
|
135
|
+
* @internal
|
|
136
|
+
*/
|
|
137
|
+
export type FormFields = Record<string, FormValue>;
|
|
138
|
+
/**
|
|
139
|
+
* Тип для путей к полям (field paths)
|
|
140
|
+
* Используется в навигации по полям вместо any
|
|
141
|
+
* @internal
|
|
142
|
+
*/
|
|
143
|
+
export type FieldPathSegment = {
|
|
144
|
+
key: string;
|
|
145
|
+
index?: number;
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Тип для коллбэков и обработчиков событий
|
|
149
|
+
* Используется вместо (...args: any[]) => any
|
|
150
|
+
* @internal
|
|
151
|
+
*/
|
|
152
|
+
export type UnknownCallback = (...args: unknown[]) => unknown;
|
|
153
|
+
/**
|
|
154
|
+
* Тип для проверки на функцию в conditional types
|
|
155
|
+
* Используется вместо Function для type narrowing
|
|
156
|
+
* @internal
|
|
157
|
+
*/
|
|
158
|
+
export type AnyFunction = (...args: never[]) => unknown;
|
|
159
|
+
/**
|
|
160
|
+
* Тип для результатов загрузки ресурсов
|
|
161
|
+
* @internal
|
|
162
|
+
*/
|
|
163
|
+
export type ResourceLoadResult = unknown;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Типы для validation schema паттерна
|
|
3
|
+
*
|
|
4
|
+
* Основано на Angular Signal Forms подходе:
|
|
5
|
+
* - Валидация определяется отдельно от схемы полей
|
|
6
|
+
* - Поддержка условной валидации (applyWhen)
|
|
7
|
+
* - Cross-field валидация (validateTree)
|
|
8
|
+
* - Асинхронная валидация с контекстом
|
|
9
|
+
*/
|
|
10
|
+
import type { FormFields, ValidationError } from './index';
|
|
11
|
+
import type { FieldPath } from './field-path';
|
|
12
|
+
import type { FormContext } from './form-context';
|
|
13
|
+
/**
|
|
14
|
+
* Функция валидации поля с контекстом
|
|
15
|
+
*
|
|
16
|
+
* Новый паттерн: (value, ctx: FormContext) => ValidationError | null
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* validate(path.email, (value, ctx) => {
|
|
21
|
+
* if (!value) return { code: 'required', message: 'Email required' };
|
|
22
|
+
* const confirm = ctx.form.confirmEmail.value.value;
|
|
23
|
+
* if (value !== confirm) return { code: 'mismatch', message: 'Must match' };
|
|
24
|
+
* return null;
|
|
25
|
+
* });
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export type ContextualValidatorFn<TForm, TField> = (value: TField, ctx: FormContext<TForm>) => ValidationError | null;
|
|
29
|
+
/**
|
|
30
|
+
* Асинхронная функция валидации поля с контекстом
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* validateAsync(path.email, async (value, ctx) => {
|
|
35
|
+
* const exists = await checkEmailExists(value);
|
|
36
|
+
* if (exists) return { code: 'exists', message: 'Email already taken' };
|
|
37
|
+
* return null;
|
|
38
|
+
* });
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export type ContextualAsyncValidatorFn<TForm, TField> = (value: TField, ctx: FormContext<TForm>) => Promise<ValidationError | null>;
|
|
42
|
+
/**
|
|
43
|
+
* Функция cross-field валидации
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* validateTree((ctx) => {
|
|
48
|
+
* const password = ctx.form.password.value.value;
|
|
49
|
+
* const confirm = ctx.form.confirmPassword.value.value;
|
|
50
|
+
* if (password !== confirm) {
|
|
51
|
+
* return { code: 'mismatch', message: 'Passwords must match' };
|
|
52
|
+
* }
|
|
53
|
+
* return null;
|
|
54
|
+
* });
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export type TreeValidatorFn<TForm> = (ctx: FormContext<TForm>) => ValidationError | null;
|
|
58
|
+
/**
|
|
59
|
+
* Функция условия для applyWhen
|
|
60
|
+
*/
|
|
61
|
+
export type ConditionFn<T> = (value: T) => boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Опции для функции validate
|
|
64
|
+
*/
|
|
65
|
+
export interface ValidateOptions {
|
|
66
|
+
/** Сообщение об ошибке */
|
|
67
|
+
message?: string;
|
|
68
|
+
/** Параметры ошибки */
|
|
69
|
+
params?: FormFields;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Опции для функции validateAsync
|
|
73
|
+
*/
|
|
74
|
+
export interface ValidateAsyncOptions extends ValidateOptions {
|
|
75
|
+
/** Задержка перед выполнением валидации (в мс) */
|
|
76
|
+
debounce?: number;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Опции для функции validateTree
|
|
80
|
+
*/
|
|
81
|
+
export interface ValidateTreeOptions {
|
|
82
|
+
/** Поле, к которому привязать ошибку */
|
|
83
|
+
targetField?: string;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Функция validation schema
|
|
87
|
+
*
|
|
88
|
+
* Принимает FieldPath и определяет все правила валидации для формы
|
|
89
|
+
*/
|
|
90
|
+
export type ValidationSchemaFn<T> = (path: FieldPath<T>) => void;
|
|
91
|
+
/**
|
|
92
|
+
* Регистрация валидатора в системе
|
|
93
|
+
* @internal
|
|
94
|
+
*/
|
|
95
|
+
export interface ValidatorRegistration {
|
|
96
|
+
fieldPath: string;
|
|
97
|
+
type: 'sync' | 'async' | 'tree';
|
|
98
|
+
validator: ContextualValidatorFn<unknown, unknown> | ContextualAsyncValidatorFn<unknown, unknown> | TreeValidatorFn<unknown>;
|
|
99
|
+
options?: ValidateOptions | ValidateAsyncOptions | ValidateTreeOptions;
|
|
100
|
+
condition?: {
|
|
101
|
+
fieldPath: string;
|
|
102
|
+
conditionFn: ConditionFn<unknown>;
|
|
103
|
+
};
|
|
104
|
+
}
|