@reformer/core 9.1.1 → 10.0.0-beta.1
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/README.md +39 -28
- package/dist/behaviors-node-DUrqAqp1.js +18 -0
- package/dist/behaviors.js +1 -1
- package/dist/form/create-form.d.ts +13 -17
- package/dist/form/index.d.ts +4 -6
- package/dist/form/nodes/group-node.d.ts +2 -15
- package/dist/form/types/schema-node.d.ts +13 -9
- package/dist/form/types/validation-schema.d.ts +7 -29
- package/dist/form/validation/index.d.ts +7 -0
- package/dist/form/validation/validators/email.d.ts +1 -1
- package/dist/form/validation/validators/file-type.d.ts +31 -0
- package/dist/form/validation/validators/file-utils.d.ts +51 -0
- package/dist/form/validation/validators/future-date.d.ts +1 -1
- package/dist/form/validation/validators/index.d.ts +7 -0
- package/dist/form/validation/validators/is-date.d.ts +1 -1
- package/dist/form/validation/validators/max-file-size.d.ts +28 -0
- package/dist/form/validation/validators/max-files.d.ts +26 -0
- package/dist/form/validation/validators/max-total-file-size.d.ts +26 -0
- package/dist/form/validation/validators/min-file-size.d.ts +28 -0
- package/dist/form/validation/validators/min-files.d.ts +26 -0
- package/dist/form/validation/validators/past-date.d.ts +1 -1
- package/dist/form/validation/validators/pattern.d.ts +1 -1
- package/dist/form/validation/validators/phone.d.ts +1 -1
- package/dist/form/validation/validators/url.d.ts +1 -1
- package/dist/form/validation-schema.d.ts +93 -0
- package/dist/index.js +352 -454
- package/dist/signal-node-registry-tiwel-TV.js +11 -0
- package/dist/state/behaviors-value.d.ts +3 -3
- package/dist/validation.d.ts +2 -0
- package/dist/validation.js +115 -0
- package/dist/validators.js +196 -65
- package/llms.txt +1198 -582
- package/package.json +5 -1
- package/dist/behaviors-node-BMOI2JJF.js +0 -26
- package/dist/form/validate-model-core.d.ts +0 -72
- package/dist/form/validate-model.d.ts +0 -27
package/README.md
CHANGED
|
@@ -30,19 +30,21 @@ npm install @reformer/core@beta # Active development is underway, so you can try
|
|
|
30
30
|
|
|
31
31
|
## Quick Start
|
|
32
32
|
|
|
33
|
-
ReFormer is built around the **M1 architecture**: a reactive `FormModel` owns the values, a
|
|
34
|
-
schema binds field config (component /
|
|
35
|
-
wires them into a typed form.
|
|
33
|
+
ReFormer is built around the **M1 architecture**: a reactive `FormModel` owns the values, a layout
|
|
34
|
+
schema binds field config (component / props) to the model's signals, and `createForm({ model, schema })`
|
|
35
|
+
wires them into a typed form. Validation is a **separate** ambient contract — `defineValidationSchema`,
|
|
36
|
+
run on demand by `validateModel(model, schema)` — so the layout schema carries no validators.
|
|
36
37
|
|
|
37
38
|
```tsx
|
|
38
39
|
import { useMemo } from 'react';
|
|
39
40
|
import {
|
|
40
41
|
createModel,
|
|
41
42
|
createForm,
|
|
42
|
-
validateFormModel,
|
|
43
43
|
useFormControl,
|
|
44
44
|
type FieldNode,
|
|
45
|
+
type ValidationError,
|
|
45
46
|
} from '@reformer/core';
|
|
47
|
+
import { validate, cross, defineValidationSchema, validateModel } from '@reformer/core/validation';
|
|
46
48
|
import { required, email, minLength } from '@reformer/core/validators';
|
|
47
49
|
import { defineFormBehavior, onChange } from '@reformer/core/behaviors';
|
|
48
50
|
|
|
@@ -79,45 +81,54 @@ const model = createModel<RegistrationForm>({
|
|
|
79
81
|
confirmPassword: '',
|
|
80
82
|
});
|
|
81
83
|
|
|
82
|
-
// 3.
|
|
83
|
-
//
|
|
84
|
-
//
|
|
84
|
+
// 3. Layout schema — binds field config (component / props) to model signals
|
|
85
|
+
// (`model.$.<field>`). Layout carries NO validators; rules live in the
|
|
86
|
+
// validation schema below.
|
|
85
87
|
const schema = {
|
|
86
88
|
children: [
|
|
87
|
-
{ value: model.$.username, component: Input
|
|
88
|
-
{ value: model.$.email, component: Input
|
|
89
|
-
{ value: model.$.password, component: Input
|
|
90
|
-
{
|
|
91
|
-
value: model.$.confirmPassword,
|
|
92
|
-
component: Input,
|
|
93
|
-
validators: [
|
|
94
|
-
required(),
|
|
95
|
-
(value, m) =>
|
|
96
|
-
value && m.password && value !== m.password
|
|
97
|
-
? { code: 'mismatch', message: 'Passwords do not match' }
|
|
98
|
-
: null,
|
|
99
|
-
],
|
|
100
|
-
},
|
|
89
|
+
{ value: model.$.username, component: Input },
|
|
90
|
+
{ value: model.$.email, component: Input },
|
|
91
|
+
{ value: model.$.password, component: Input },
|
|
92
|
+
{ value: model.$.confirmPassword, component: Input },
|
|
101
93
|
],
|
|
102
94
|
};
|
|
103
95
|
|
|
104
|
-
// 4.
|
|
105
|
-
//
|
|
106
|
-
|
|
96
|
+
// 4. Validation schema — a separate ambient contract from `@reformer/core/validation`,
|
|
97
|
+
// run on demand (not reactive). Field rules go through `validate(sig, [rules])`;
|
|
98
|
+
// cross-field rules through `cross(sig, snapshot => error | null)` where the snapshot
|
|
99
|
+
// is `model.get()`. Rule factories (`required()`/…) come from `@reformer/core/validators`.
|
|
100
|
+
const passwordsMatch = (f: RegistrationForm): ValidationError | null =>
|
|
101
|
+
f.confirmPassword && f.password && f.confirmPassword !== f.password
|
|
102
|
+
? { code: 'mismatch', message: 'Passwords do not match' }
|
|
103
|
+
: null;
|
|
104
|
+
|
|
105
|
+
const validationSchema = defineValidationSchema<RegistrationForm>(({ model }) => {
|
|
106
|
+
validate(model.$.username, [required(), minLength(2)]);
|
|
107
|
+
validate(model.$.email, [required(), email()]);
|
|
108
|
+
validate(model.$.password, [required(), minLength(8)]);
|
|
109
|
+
validate(model.$.confirmPassword, [required()]);
|
|
110
|
+
cross(model.$.confirmPassword, passwordsMatch);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// 5. Behavior — declarative reactive logic, run by `createForm({ behavior })`.
|
|
114
|
+
// Validation runs on demand, so the stale cross-field error on `confirmPassword`
|
|
115
|
+
// is cleared the moment `password` changes — a behavior→validation bridge.
|
|
116
|
+
const behavior = defineFormBehavior<RegistrationForm>(({ model, form }) => {
|
|
107
117
|
onChange(model.$.password, () => {
|
|
108
|
-
|
|
118
|
+
form.confirmPassword.clearErrors();
|
|
109
119
|
});
|
|
110
120
|
});
|
|
111
121
|
|
|
112
|
-
//
|
|
122
|
+
// 6. Registration form component
|
|
113
123
|
function RegistrationFormExample() {
|
|
114
124
|
const form = useMemo(() => createForm<RegistrationForm>({ model, schema, behavior }), []);
|
|
115
125
|
|
|
116
126
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
117
127
|
e.preventDefault();
|
|
118
128
|
form.touchAll();
|
|
119
|
-
// Validate the whole model against the schema (sync + async)
|
|
120
|
-
|
|
129
|
+
// Validate the whole model against the validation schema (sync + async) on demand;
|
|
130
|
+
// errors route into the form nodes, so the UI highlights the offending fields.
|
|
131
|
+
const valid = await validateModel(model, validationSchema);
|
|
121
132
|
if (valid) {
|
|
122
133
|
console.log('Form data:', model.get());
|
|
123
134
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { effect as i } from "@preact/signals-core";
|
|
2
|
+
import { g as s } from "./signal-node-registry-tiwel-TV.js";
|
|
3
|
+
import { d as f } from "./behaviors-value-lCpqicfa.js";
|
|
4
|
+
function a(n, r, t) {
|
|
5
|
+
return i(() => {
|
|
6
|
+
const o = r(), e = s(n);
|
|
7
|
+
e && f(() => {
|
|
8
|
+
o ? e.enable() : (e.disable(), t?.resetOnDisable && e.reset());
|
|
9
|
+
});
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
function b(n, r, t) {
|
|
13
|
+
return a(n, () => !r(), t);
|
|
14
|
+
}
|
|
15
|
+
export {
|
|
16
|
+
b as d,
|
|
17
|
+
a as e
|
|
18
|
+
};
|
package/dist/behaviors.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { effect as O } from "@preact/signals-core";
|
|
2
2
|
import { d as $, m as k, u as x, a as S, w as F, t as A, r as C, s as D, b as M } from "./behaviors-value-lCpqicfa.js";
|
|
3
|
-
import { e as G } from "./behaviors-node-
|
|
3
|
+
import { e as G } from "./behaviors-node-DUrqAqp1.js";
|
|
4
4
|
let p = null;
|
|
5
5
|
function P(e) {
|
|
6
6
|
if (!p)
|
|
@@ -73,12 +73,13 @@ export declare function createFormFromModel<T>(args: CreateFormFromModelArgs<T>)
|
|
|
73
73
|
*
|
|
74
74
|
* @group Utilities
|
|
75
75
|
*
|
|
76
|
-
* @param args - Модель данных,
|
|
76
|
+
* @param args - Модель данных, layout-схема (component/componentProps) и (опционально) поведение
|
|
77
77
|
* @returns Типизированная форма с Proxy-доступом к полям
|
|
78
78
|
*
|
|
79
|
-
* @example Архитектура M1: `createModel` +
|
|
79
|
+
* @example Архитектура M1: `createModel` + layout-схема + `createForm({ model, schema })`
|
|
80
80
|
* ```typescript
|
|
81
|
-
* import { createModel, createForm
|
|
81
|
+
* import { createModel, createForm } from '@reformer/core';
|
|
82
|
+
* import { defineValidationSchema, validate, validateModel } from '@reformer/core/validation';
|
|
82
83
|
* import { required, email, minLength } from '@reformer/core/validators';
|
|
83
84
|
*
|
|
84
85
|
* interface UserForm {
|
|
@@ -88,28 +89,23 @@ export declare function createFormFromModel<T>(args: CreateFormFromModelArgs<T>)
|
|
|
88
89
|
*
|
|
89
90
|
* const model = createModel<UserForm>({ email: '', password: '' });
|
|
90
91
|
*
|
|
92
|
+
* // Layout-схема НЕ несёт validators — только component/componentProps.
|
|
91
93
|
* const schema = {
|
|
92
94
|
* children: [
|
|
93
|
-
* {
|
|
94
|
-
*
|
|
95
|
-
* component: Input,
|
|
96
|
-
* validators: [required(), email()],
|
|
97
|
-
* },
|
|
98
|
-
* {
|
|
99
|
-
* value: model.$.password,
|
|
100
|
-
* component: Input,
|
|
101
|
-
* validators: [required(), minLength(8)],
|
|
102
|
-
* },
|
|
95
|
+
* { value: model.$.email, component: Input },
|
|
96
|
+
* { value: model.$.password, component: Input },
|
|
103
97
|
* ],
|
|
104
98
|
* };
|
|
105
99
|
*
|
|
106
100
|
* const form = createForm<UserForm>({ model, schema });
|
|
107
|
-
*
|
|
108
|
-
* // TypeScript знает о полях (нода привязана к сигналу модели):
|
|
109
101
|
* form.email.setValue('test@mail.com');
|
|
110
102
|
*
|
|
111
|
-
* // Валидация
|
|
112
|
-
* const
|
|
103
|
+
* // Валидация — ОТДЕЛЬНАЯ схема, прогон внешним раннером (ошибки сами доезжают до нод):
|
|
104
|
+
* const validation = defineValidationSchema<UserForm>(({ model }) => {
|
|
105
|
+
* validate(model.$.email, [required(), email()]);
|
|
106
|
+
* validate(model.$.password, [required(), minLength(8)]);
|
|
107
|
+
* });
|
|
108
|
+
* const ok = await validateModel(model, validation);
|
|
113
109
|
* ```
|
|
114
110
|
*/
|
|
115
111
|
export declare function createForm<T>(args: CreateFormFromModelArgs<T>): FormProxy<T>;
|
package/dist/form/index.d.ts
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
* Модуль `form` — доменный слой формы поверх state-субстрата (`model`).
|
|
3
3
|
*
|
|
4
4
|
* Ноды (value/touched/dirty/status/errors/componentProps), сборка формы из модели по идентичности
|
|
5
|
-
* сигнала (`createForm`), state-операции над нодами (`enableWhen`/`disableWhen`),
|
|
6
|
-
*
|
|
7
|
-
* разрешено);
|
|
5
|
+
* сигнала (`createForm`), state-операции над нодами (`enableWhen`/`disableWhen`), submit/статус,
|
|
6
|
+
* React-хуки. Schema-валидация — внешний контракт `@reformer/core/validation` (`validateModel`),
|
|
7
|
+
* роутит ошибки в ноды через реестр сигнал→нода. Зависит от `model` (form→state разрешено);
|
|
8
|
+
* обратной зависимости нет.
|
|
8
9
|
*
|
|
9
10
|
* @group Form
|
|
10
11
|
* @module form
|
|
@@ -18,9 +19,6 @@ export { ModelArrayNode } from './nodes/model-array-node';
|
|
|
18
19
|
export type { ModelArrayControl } from './nodes/model-array-node';
|
|
19
20
|
export { createForm, createFormFromModel } from './create-form';
|
|
20
21
|
export type { CreateFormFromModelArgs } from './create-form';
|
|
21
|
-
export { validateModel, validateModelSync } from './validate-model-core';
|
|
22
|
-
export type { ModelValidator, ModelValidationResult } from './validate-model-core';
|
|
23
|
-
export { validateFormModel } from './validate-model';
|
|
24
22
|
export { enableWhen, disableWhen } from './behaviors-node';
|
|
25
23
|
export { FormErrorHandler, ErrorStrategy } from './error-handler';
|
|
26
24
|
export { registerSignalNode, getNodeForSignal } from './signal-node-registry';
|
|
@@ -7,8 +7,8 @@ import { SubmitOptions, SubmitResult } from '../form-submitter';
|
|
|
7
7
|
* GroupNode - узел для группы полей
|
|
8
8
|
*
|
|
9
9
|
* Создаётся из {@link FormSchema} (дерево field-конфигов). Обычно строится через `createForm`
|
|
10
|
-
* (M1: `createForm({ model, schema })`);
|
|
11
|
-
* (`
|
|
10
|
+
* (M1: `createForm({ model, schema })`); schema-валидация/behavior живут на слое модели
|
|
11
|
+
* (`validateModel` из `@reformer/core/validation`, `computeFrom`/`enableWhen`/…), а не на ноде.
|
|
12
12
|
*
|
|
13
13
|
* @group Nodes
|
|
14
14
|
*
|
|
@@ -54,12 +54,6 @@ export declare class GroupNode<T> extends FormNode<T> {
|
|
|
54
54
|
private readonly _disabled;
|
|
55
55
|
/** Form-level validation errors */
|
|
56
56
|
private readonly _formErrors;
|
|
57
|
-
/**
|
|
58
|
-
* M1: валидация уровня модели/схемы (`validateFormModel(model, schema)`). Привязывается
|
|
59
|
-
* `createForm({ model, schema })`, т.к. под M1 валидаторы срезаны с FieldNode и живут на слое
|
|
60
|
-
* модели. Без неё `validate()`/`submit()` пропускали бы все schema-валидаторы. @internal
|
|
61
|
-
*/
|
|
62
|
-
private _modelValidate?;
|
|
63
57
|
/**
|
|
64
58
|
* M1: связь листовой ноды-ребёнка → её сигнал модели (тот, что помечает `markDerived`).
|
|
65
59
|
* Нужна bulk-сеттерам (`setValue`/`patchValue`), чтобы корректно определять derived-поля:
|
|
@@ -281,13 +275,6 @@ export declare class GroupNode<T> extends FormNode<T> {
|
|
|
281
275
|
* @internal
|
|
282
276
|
*/
|
|
283
277
|
attachBehaviorCleanup(cleanup: () => void): void;
|
|
284
|
-
/**
|
|
285
|
-
* Прикрепить валидацию уровня модели/схемы (M1). Вызывается `createForm({ model, schema })`,
|
|
286
|
-
* чтобы `validate()`/`submit()` прогоняли schema-валидаторы (`validateFormModel`), а не только
|
|
287
|
-
* пустые FieldNode. Возвращаемый флаг — валидна ли модель по схеме.
|
|
288
|
-
* @internal
|
|
289
|
-
*/
|
|
290
|
-
attachModelValidator(validate: () => Promise<boolean>): void;
|
|
291
278
|
/**
|
|
292
279
|
* Связать листовую ноду-ребёнка с её сигналом модели (M1). Вызывается `createForm` для каждого
|
|
293
280
|
* листового поля на его владеющей группе — используется bulk-сеттерами для derived-guard (F9).
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ElementType } from 'react';
|
|
2
2
|
import { ValidationError } from './contracts';
|
|
3
3
|
/**
|
|
4
|
-
* Валидатор поля
|
|
5
|
-
* `(value, scope, root)`,
|
|
6
|
-
* `
|
|
7
|
-
*
|
|
4
|
+
* Валидатор поля схемы (legacy). Дерево-движок, вызывавший `validators` узла как
|
|
5
|
+
* `(value, scope, root)`, удалён — живые правила это `Rule<T>` в `validate(sig, [...])`
|
|
6
|
+
* (`@reformer/core/validation`). Тип остаётся широким по параметрам для совместимости
|
|
7
|
+
* узлов, всё ещё несущих `validators` (гетерогенные контракты `ValidatorFn`/`Validator`).
|
|
8
8
|
*
|
|
9
9
|
* @group Types
|
|
10
10
|
*/
|
|
@@ -26,8 +26,8 @@ export interface SchemaArrayControl {
|
|
|
26
26
|
removeAt(index: number): void;
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
|
-
* Узел единой схемы M1 —
|
|
30
|
-
*
|
|
29
|
+
* Узел единой схемы M1 — layout-дерево, обходимое `createForm({ model, schema })`
|
|
30
|
+
* и рендерерами (schema-валидация живёт отдельно — `@reformer/core/validation`).
|
|
31
31
|
*
|
|
32
32
|
* Узел совмещает несколько ролей (различаются рантаймом по форме):
|
|
33
33
|
* - **поле** — несёт `value: Signal` (сигнал модели `model.$.x`) + `component`/`validators`;
|
|
@@ -47,8 +47,12 @@ export interface FormSchemaNode {
|
|
|
47
47
|
* `Signal`). Движок разбирает узел как поле рантаймом по `value instanceof Signal`.
|
|
48
48
|
*/
|
|
49
49
|
value?: unknown;
|
|
50
|
-
/**
|
|
51
|
-
|
|
50
|
+
/**
|
|
51
|
+
* UI-компонент либо нативный HTML-тег (`'div'`, `'p'`, `'h3'`) для презентационной вёрстки
|
|
52
|
+
* прямо в схеме. Опционален: core-часть работает без UI (значение/валидация) и `component`
|
|
53
|
+
* не интерпретирует — он доезжает до рендерера как есть.
|
|
54
|
+
*/
|
|
55
|
+
component?: ElementType;
|
|
52
56
|
/** Props компонента. Также «клапан» для вложенности под-узлов (напр. steps визарда). */
|
|
53
57
|
componentProps?: Record<string, unknown>;
|
|
54
58
|
validators?: SchemaValidator[];
|
|
@@ -1,36 +1,14 @@
|
|
|
1
1
|
import { FormValue, ValidationError } from './index';
|
|
2
2
|
import { FormModel } from '../../state/types';
|
|
3
3
|
/**
|
|
4
|
-
* Чистый синхронный валидатор
|
|
4
|
+
* Чистый синхронный валидатор поля (legacy-сигнатура `(value, scope, root)`).
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* -
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* - `root` — корневая модель формы `FormModel<TForm>` (реактивный value-proxy: `root.field` читает
|
|
13
|
-
* значение). Возвращает `ValidationError` либо `null`. Не знает про реестр валидации.
|
|
14
|
-
*
|
|
15
|
-
* Совместим с полем `validators` узла схемы (см. `SchemaValidator`) — там же лежат `ModelValidator`
|
|
16
|
-
* и `ValidatorFn`. Встроенные фабрики (`required()`/`email()`/…) возвращают `(value) => …` и
|
|
17
|
-
* дополнительные аргументы игнорируют.
|
|
18
|
-
*
|
|
19
|
-
* @example Кастомный валидатор в массиве `validators` поля схемы
|
|
20
|
-
* ```typescript
|
|
21
|
-
* const isAdult: Validator<MyForm, number> = (value, _scope, root) => {
|
|
22
|
-
* if (value < 18) return { code: 'tooYoung', message: '18+' };
|
|
23
|
-
* // root — FormModel<MyForm>: root.someOtherField читается как значение
|
|
24
|
-
* return null;
|
|
25
|
-
* };
|
|
26
|
-
*
|
|
27
|
-
* // Фабрики и кастомные валидаторы кладутся в `validators: [...]` поля:
|
|
28
|
-
* const schema = {
|
|
29
|
-
* children: [
|
|
30
|
-
* { value: model.$.age, component: Input, validators: [required(), isAdult] },
|
|
31
|
-
* ],
|
|
32
|
-
* };
|
|
33
|
-
* ```
|
|
6
|
+
* @deprecated Осиротевший остаток удалённого дерево-движка (`validateFormModel`). Живой контракт —
|
|
7
|
+
* `Rule<T> = (value) => ValidationError | null` из `@reformer/core/validation`: правила
|
|
8
|
+
* передаются в `validate(sig, [rules])` внутри `defineValidationSchema` и запускаются
|
|
9
|
+
* `validateModel(model, schema)`. Cross-field — оператор `cross(sig, (f) => …)` над снапшотом
|
|
10
|
+
* `model.get()` (третий аргумент `root` больше не нужен). Тип экспортируется только ради
|
|
11
|
+
* обратной совместимости (`SchemaValidator`-union).
|
|
34
12
|
*/
|
|
35
13
|
export type Validator<TForm, TField> = (value: TField, scope: unknown, root: FormModel<TForm>) => ValidationError | null;
|
|
36
14
|
/**
|
|
@@ -19,3 +19,10 @@ export { pastDate } from './validators/past-date';
|
|
|
19
19
|
export { futureDate } from './validators/future-date';
|
|
20
20
|
export { minAge } from './validators/min-age';
|
|
21
21
|
export { maxAge } from './validators/max-age';
|
|
22
|
+
export { maxFileSize } from './validators/max-file-size';
|
|
23
|
+
export { minFileSize } from './validators/min-file-size';
|
|
24
|
+
export { fileType } from './validators/file-type';
|
|
25
|
+
export { maxFiles } from './validators/max-files';
|
|
26
|
+
export { minFiles } from './validators/min-files';
|
|
27
|
+
export { maxTotalFileSize } from './validators/max-total-file-size';
|
|
28
|
+
export { isFileLike, matchesFileAccept, toFileArray, type FileLike } from './validators/file-utils';
|
|
@@ -20,4 +20,4 @@ import { Validator, ValidateOptions } from '../../types/validation-schema';
|
|
|
20
20
|
* },
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
|
-
export declare function email<TForm = unknown, TField extends string | undefined = string>(options?: ValidateOptions): Validator<TForm, TField>;
|
|
23
|
+
export declare function email<TForm = unknown, TField extends string | null | undefined = string>(options?: ValidateOptions): Validator<TForm, TField>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Validator, ValidateOptions } from '../../types/validation-schema';
|
|
2
|
+
/**
|
|
3
|
+
* Фабрика валидатора типа файла.
|
|
4
|
+
*
|
|
5
|
+
* Проверяет каждый файл против accept-строки в синтаксисе нативного атрибута
|
|
6
|
+
* `<input type="file" accept>`: расширения (`.pdf`), точные MIME (`image/png`),
|
|
7
|
+
* wildcard-категории (`image/*`), список через запятую. Матчинг регистронезависимый,
|
|
8
|
+
* по `type` и/или расширению из `name` ({@link matchesFileAccept}).
|
|
9
|
+
*
|
|
10
|
+
* Нужен потому, что нативный `accept` — только подсказка пикеру: на drag-and-drop
|
|
11
|
+
* и «All files» он не действует. Пустые значения (`null`/`undefined`/`''`/`[]`)
|
|
12
|
+
* пропускаются (используйте {@link required} для обязательности).
|
|
13
|
+
*
|
|
14
|
+
* @param accept - Accept-строка допустимых типов, например `'image/*,.pdf'`
|
|
15
|
+
* @param options - Опции валидатора ({@link ValidateOptions}). В `params` ошибки автоматически
|
|
16
|
+
* попадают `accept` и `fileName` (первый нарушивший файл).
|
|
17
|
+
* @returns Чистый валидатор {@link Validator} для файла или массива файлов
|
|
18
|
+
*
|
|
19
|
+
* @example Только изображения и PDF
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import { fileType } from '@reformer/core/validators';
|
|
22
|
+
*
|
|
23
|
+
* // Внутри FieldConfig схемы формы:
|
|
24
|
+
* documents: {
|
|
25
|
+
* value: model.$.documents,
|
|
26
|
+
* component: FileUploadField,
|
|
27
|
+
* validators: [fileType('image/*,.pdf', { message: 'Только изображения или PDF' })],
|
|
28
|
+
* },
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare function fileType<TForm = unknown, TField = unknown>(accept: string, options?: ValidateOptions): Validator<TForm, TField>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Утилиты для работы с файлами в валидаторах.
|
|
3
|
+
*
|
|
4
|
+
* Duck-typing вместо `instanceof File` — SSR-безопасность (в Node может не быть глобального
|
|
5
|
+
* `File`) и поддержка сериализуемых дескрипторов загруженных файлов (`RemoteFileRef` из
|
|
6
|
+
* `@reformer/cdk/file-upload`) наравне с нативными `File`.
|
|
7
|
+
*
|
|
8
|
+
* @group Validation
|
|
9
|
+
* @category Validators
|
|
10
|
+
* @module validators/file-utils
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Минимальный файлоподобный контракт, с которым работают file-валидаторы:
|
|
14
|
+
* нативный `File`, сериализуемый дескриптор загруженного файла и т.п.
|
|
15
|
+
*/
|
|
16
|
+
export interface FileLike {
|
|
17
|
+
name: string;
|
|
18
|
+
size?: number;
|
|
19
|
+
type?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Проверяет, что значение файлоподобно: объект со строковым `name` и опциональными
|
|
23
|
+
* числовым `size` / строковым `type`.
|
|
24
|
+
*
|
|
25
|
+
* @param value - Проверяемое значение
|
|
26
|
+
* @returns `true`, если значение соответствует {@link FileLike}
|
|
27
|
+
*/
|
|
28
|
+
export declare function isFileLike(value: unknown): value is FileLike;
|
|
29
|
+
/**
|
|
30
|
+
* Нормализует значение file-поля в массив файлоподобных элементов.
|
|
31
|
+
*
|
|
32
|
+
* Одиночный {@link FileLike} оборачивается в массив; из массива отбираются только
|
|
33
|
+
* файлоподобные элементы. Для прочих значений — `null` (валидатор пропускает).
|
|
34
|
+
*
|
|
35
|
+
* @param value - Значение поля (`FileLike`, `FileLike[]` или что-то ещё)
|
|
36
|
+
* @returns Массив файлоподобных элементов или `null`
|
|
37
|
+
*/
|
|
38
|
+
export declare function toFileArray(value: unknown): FileLike[] | null;
|
|
39
|
+
/**
|
|
40
|
+
* Проверяет файл против accept-паттерна в синтаксисе нативного атрибута
|
|
41
|
+
* `<input type="file" accept>`: список через запятую из расширений (`.pdf`),
|
|
42
|
+
* точных MIME (`image/png`) и wildcard-категорий (`image/*`). Регистронезависимо.
|
|
43
|
+
*
|
|
44
|
+
* Нужен собственный матчер, потому что `accept` у нативного input — только подсказка
|
|
45
|
+
* пикеру: на drag-and-drop и на программное значение он не действует.
|
|
46
|
+
*
|
|
47
|
+
* @param file - Файлоподобное значение (`name` + опциональный `type`)
|
|
48
|
+
* @param accept - Accept-строка; пустая/пробельная строка матчит всё
|
|
49
|
+
* @returns `true`, если файл подходит хотя бы под один паттерн
|
|
50
|
+
*/
|
|
51
|
+
export declare function matchesFileAccept(file: Pick<FileLike, 'name' | 'type'>, accept: string): boolean;
|
|
@@ -20,4 +20,4 @@ import { Validator, ValidateOptions } from '../../types/validation-schema';
|
|
|
20
20
|
* },
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
|
-
export declare function futureDate<TForm = unknown, TField extends string | Date | undefined = string | Date>(options?: ValidateOptions): Validator<TForm, TField>;
|
|
23
|
+
export declare function futureDate<TForm = unknown, TField extends string | Date | null | undefined = string | Date>(options?: ValidateOptions): Validator<TForm, TField>;
|
|
@@ -26,3 +26,10 @@ export { pastDate } from './past-date';
|
|
|
26
26
|
export { futureDate } from './future-date';
|
|
27
27
|
export { minAge } from './min-age';
|
|
28
28
|
export { maxAge } from './max-age';
|
|
29
|
+
export { maxFileSize } from './max-file-size';
|
|
30
|
+
export { minFileSize } from './min-file-size';
|
|
31
|
+
export { fileType } from './file-type';
|
|
32
|
+
export { maxFiles } from './max-files';
|
|
33
|
+
export { minFiles } from './min-files';
|
|
34
|
+
export { maxTotalFileSize } from './max-total-file-size';
|
|
35
|
+
export { isFileLike, matchesFileAccept, toFileArray, type FileLike } from './file-utils';
|
|
@@ -20,4 +20,4 @@ import { Validator, ValidateOptions } from '../../types/validation-schema';
|
|
|
20
20
|
* },
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
|
-
export declare function isDate<TForm = unknown, TField extends string | Date | undefined = string | Date>(options?: ValidateOptions): Validator<TForm, TField>;
|
|
23
|
+
export declare function isDate<TForm = unknown, TField extends string | Date | null | undefined = string | Date>(options?: ValidateOptions): Validator<TForm, TField>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Validator, ValidateOptions } from '../../types/validation-schema';
|
|
2
|
+
/**
|
|
3
|
+
* Фабрика валидатора максимального размера файла.
|
|
4
|
+
*
|
|
5
|
+
* Работает с одиночным файлом или массивом (`File` либо файлоподобный дескриптор с
|
|
6
|
+
* `name`/`size`). Проверяется каждый файл; в ошибку попадает первый нарушивший.
|
|
7
|
+
* Пустые значения (`null`/`undefined`/`''`/`[]`) и элементы без числового `size`
|
|
8
|
+
* (например, дескриптор загруженного файла без размера) пропускаются
|
|
9
|
+
* (используйте {@link required} для обязательности).
|
|
10
|
+
*
|
|
11
|
+
* @param maxSize - Максимально допустимый размер файла в байтах (включительно)
|
|
12
|
+
* @param options - Опции валидатора ({@link ValidateOptions}). В `params` ошибки автоматически
|
|
13
|
+
* попадают `maxFileSize`, `fileName` и `actualSize`.
|
|
14
|
+
* @returns Чистый валидатор {@link Validator} для файла или массива файлов
|
|
15
|
+
*
|
|
16
|
+
* @example Ограничение размера вложений
|
|
17
|
+
* ```typescript
|
|
18
|
+
* import { maxFileSize } from '@reformer/core/validators';
|
|
19
|
+
*
|
|
20
|
+
* // Внутри FieldConfig схемы формы:
|
|
21
|
+
* documents: {
|
|
22
|
+
* value: model.$.documents,
|
|
23
|
+
* component: FileUploadField,
|
|
24
|
+
* validators: [maxFileSize(5 * 1024 * 1024, { message: 'Файл больше 5 МБ' })],
|
|
25
|
+
* },
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare function maxFileSize<TForm = unknown, TField = unknown>(maxSize: number, options?: ValidateOptions): Validator<TForm, TField>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Validator, ValidateOptions } from '../../types/validation-schema';
|
|
2
|
+
/**
|
|
3
|
+
* Фабрика валидатора максимального количества файлов.
|
|
4
|
+
*
|
|
5
|
+
* Работает с массивом файлов (одиночный файл считается как 1). Пустые значения
|
|
6
|
+
* (`null`/`undefined`/`''`/`[]`) пропускаются (используйте {@link required}
|
|
7
|
+
* для обязательности).
|
|
8
|
+
*
|
|
9
|
+
* @param max - Максимально допустимое количество файлов (включительно)
|
|
10
|
+
* @param options - Опции валидатора ({@link ValidateOptions}). В `params` ошибки автоматически
|
|
11
|
+
* попадают `maxFiles` и `actualCount`.
|
|
12
|
+
* @returns Чистый валидатор {@link Validator} для файла или массива файлов
|
|
13
|
+
*
|
|
14
|
+
* @example Не более трёх вложений
|
|
15
|
+
* ```typescript
|
|
16
|
+
* import { maxFiles } from '@reformer/core/validators';
|
|
17
|
+
*
|
|
18
|
+
* // Внутри FieldConfig схемы формы:
|
|
19
|
+
* documents: {
|
|
20
|
+
* value: model.$.documents,
|
|
21
|
+
* component: FileUploadField,
|
|
22
|
+
* validators: [maxFiles(3, { message: 'Максимум 3 файла' })],
|
|
23
|
+
* },
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function maxFiles<TForm = unknown, TField = unknown>(max: number, options?: ValidateOptions): Validator<TForm, TField>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Validator, ValidateOptions } from '../../types/validation-schema';
|
|
2
|
+
/**
|
|
3
|
+
* Фабрика валидатора суммарного размера всех файлов.
|
|
4
|
+
*
|
|
5
|
+
* Складывает `size` всех файлов значения (элементы без числового `size` в сумму
|
|
6
|
+
* не входят). Пустые значения (`null`/`undefined`/`''`/`[]`) пропускаются
|
|
7
|
+
* (используйте {@link required} для обязательности).
|
|
8
|
+
*
|
|
9
|
+
* @param maxTotal - Максимально допустимый суммарный размер в байтах (включительно)
|
|
10
|
+
* @param options - Опции валидатора ({@link ValidateOptions}). В `params` ошибки автоматически
|
|
11
|
+
* попадают `maxTotalFileSize` и `actualTotal`.
|
|
12
|
+
* @returns Чистый валидатор {@link Validator} для файла или массива файлов
|
|
13
|
+
*
|
|
14
|
+
* @example Суммарно не более 20 МБ
|
|
15
|
+
* ```typescript
|
|
16
|
+
* import { maxTotalFileSize } from '@reformer/core/validators';
|
|
17
|
+
*
|
|
18
|
+
* // Внутри FieldConfig схемы формы:
|
|
19
|
+
* documents: {
|
|
20
|
+
* value: model.$.documents,
|
|
21
|
+
* component: FileUploadField,
|
|
22
|
+
* validators: [maxTotalFileSize(20 * 1024 * 1024, { message: 'Суммарно не более 20 МБ' })],
|
|
23
|
+
* },
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function maxTotalFileSize<TForm = unknown, TField = unknown>(maxTotal: number, options?: ValidateOptions): Validator<TForm, TField>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Validator, ValidateOptions } from '../../types/validation-schema';
|
|
2
|
+
/**
|
|
3
|
+
* Фабрика валидатора минимального размера файла.
|
|
4
|
+
*
|
|
5
|
+
* Работает с одиночным файлом или массивом (`File` либо файлоподобный дескриптор с
|
|
6
|
+
* `name`/`size`). Проверяется каждый файл; в ошибку попадает первый нарушивший.
|
|
7
|
+
* Типовой сценарий — отсев пустых (0-байтовых) файлов: `minFileSize(1)`.
|
|
8
|
+
* Пустые значения (`null`/`undefined`/`''`/`[]`) и элементы без числового `size`
|
|
9
|
+
* пропускаются (используйте {@link required} для обязательности).
|
|
10
|
+
*
|
|
11
|
+
* @param minSize - Минимально допустимый размер файла в байтах (включительно)
|
|
12
|
+
* @param options - Опции валидатора ({@link ValidateOptions}). В `params` ошибки автоматически
|
|
13
|
+
* попадают `minFileSize`, `fileName` и `actualSize`.
|
|
14
|
+
* @returns Чистый валидатор {@link Validator} для файла или массива файлов
|
|
15
|
+
*
|
|
16
|
+
* @example Отсев пустых файлов
|
|
17
|
+
* ```typescript
|
|
18
|
+
* import { minFileSize } from '@reformer/core/validators';
|
|
19
|
+
*
|
|
20
|
+
* // Внутри FieldConfig схемы формы:
|
|
21
|
+
* documents: {
|
|
22
|
+
* value: model.$.documents,
|
|
23
|
+
* component: FileUploadField,
|
|
24
|
+
* validators: [minFileSize(1, { message: 'Файл пустой' })],
|
|
25
|
+
* },
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare function minFileSize<TForm = unknown, TField = unknown>(minSize: number, options?: ValidateOptions): Validator<TForm, TField>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Validator, ValidateOptions } from '../../types/validation-schema';
|
|
2
|
+
/**
|
|
3
|
+
* Фабрика валидатора минимального количества файлов.
|
|
4
|
+
*
|
|
5
|
+
* Работает с массивом файлов (одиночный файл считается как 1). Пустые значения
|
|
6
|
+
* (`null`/`undefined`/`''`/`[]`) пропускаются — «нужен хотя бы один файл» выражается
|
|
7
|
+
* связкой `required()` + `minFiles(n)`, как и у {@link minLength}.
|
|
8
|
+
*
|
|
9
|
+
* @param min - Минимально допустимое количество файлов (включительно)
|
|
10
|
+
* @param options - Опции валидатора ({@link ValidateOptions}). В `params` ошибки автоматически
|
|
11
|
+
* попадают `minFiles` и `actualCount`.
|
|
12
|
+
* @returns Чистый валидатор {@link Validator} для файла или массива файлов
|
|
13
|
+
*
|
|
14
|
+
* @example Не менее двух документов
|
|
15
|
+
* ```typescript
|
|
16
|
+
* import { required, minFiles } from '@reformer/core/validators';
|
|
17
|
+
*
|
|
18
|
+
* // Внутри FieldConfig схемы формы:
|
|
19
|
+
* documents: {
|
|
20
|
+
* value: model.$.documents,
|
|
21
|
+
* component: FileUploadField,
|
|
22
|
+
* validators: [required(), minFiles(2, { message: 'Приложите минимум 2 документа' })],
|
|
23
|
+
* },
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function minFiles<TForm = unknown, TField = unknown>(min: number, options?: ValidateOptions): Validator<TForm, TField>;
|
|
@@ -20,4 +20,4 @@ import { Validator, ValidateOptions } from '../../types/validation-schema';
|
|
|
20
20
|
* },
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
|
-
export declare function pastDate<TForm = unknown, TField extends string | Date | undefined = string | Date>(options?: ValidateOptions): Validator<TForm, TField>;
|
|
23
|
+
export declare function pastDate<TForm = unknown, TField extends string | Date | null | undefined = string | Date>(options?: ValidateOptions): Validator<TForm, TField>;
|
|
@@ -30,4 +30,4 @@ import { Validator, ValidateOptions } from '../../types/validation-schema';
|
|
|
30
30
|
* },
|
|
31
31
|
* ```
|
|
32
32
|
*/
|
|
33
|
-
export declare function pattern<TForm = unknown, TField extends string | undefined = string>(regex: RegExp, options?: ValidateOptions): Validator<TForm, TField>;
|
|
33
|
+
export declare function pattern<TForm = unknown, TField extends string | null | undefined = string>(regex: RegExp, options?: ValidateOptions): Validator<TForm, TField>;
|