@reformer/core 11.0.0-beta.3 → 11.0.0-beta.4
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.d.ts +1 -1
- package/dist/behaviors.js +37 -37
- package/dist/create-model-Dxh9cfLp.js +97 -0
- package/dist/form/behaviors/collections.d.ts +50 -0
- package/dist/form/behaviors/context.d.ts +23 -0
- package/dist/form/behaviors/index.d.ts +28 -0
- package/dist/form/behaviors/internals.d.ts +26 -0
- package/dist/form/{behaviors-node.d.ts → behaviors/node.d.ts} +3 -3
- package/dist/form/behaviors/operators.d.ts +51 -0
- package/dist/form/behaviors/types.d.ts +17 -0
- package/dist/form/create-core-form.d.ts +3 -3
- package/dist/form/create-form.d.ts +1 -1
- package/dist/form/index.d.ts +10 -16
- package/dist/form/nodes/model-array-node.d.ts +1 -1
- package/dist/form/types/contracts.d.ts +9 -6
- package/dist/form/types/validation-schema.d.ts +1 -1
- package/dist/form/{validation-config.d.ts → validation/config.d.ts} +3 -3
- package/dist/form/validation/context.d.ts +27 -0
- package/dist/form/{error-handler.d.ts → validation/error-handler.d.ts} +1 -1
- package/dist/form/validation/index.d.ts +23 -28
- package/dist/form/validation/operators.d.ts +42 -0
- package/dist/form/validation/run.d.ts +45 -0
- package/dist/form/{validation-strategy.d.ts → validation/strategy.d.ts} +2 -2
- package/dist/form/validation/types.d.ts +28 -0
- package/dist/form/{validation/validators → validators}/date-utils.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/email.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/file-type.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/file-utils.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/future-date.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/index.d.ts +6 -4
- package/dist/form/{validation/validators → validators}/integer.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/is-date.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/is-number.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/max-age.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/max-date.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/max-file-size.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/max-files.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/max-length.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/max-total-file-size.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/max.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/min-age.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/min-date.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/min-file-size.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/min-files.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/min-length.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/min.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/multiple-of.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/non-negative.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/non-zero.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/past-date.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/pattern.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/phone.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/required.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/url.d.ts +1 -1
- package/dist/index.d.ts +7 -4
- package/dist/index.js +246 -71
- package/dist/{state/form-model.d.ts → model/create-model.d.ts} +0 -14
- package/dist/{state → model}/index.d.ts +10 -11
- package/dist/model/model-nodes.d.ts +60 -0
- package/dist/model/model-signals-proxy.d.ts +32 -0
- package/dist/model/model-value-proxy.d.ts +3 -0
- package/dist/{state → model}/safe-effect.d.ts +1 -1
- package/dist/model-signals-proxy-mV3WGdlO.js +233 -0
- package/dist/model.d.ts +2 -0
- package/dist/model.js +21 -0
- package/dist/{form → platforms/react}/hooks/types.d.ts +1 -1
- package/dist/{form → platforms/react}/hooks/use-form-bundle.d.ts +1 -1
- package/dist/{form → platforms/react}/hooks/use-form-validation.d.ts +3 -3
- package/dist/{form → platforms/react}/hooks/useArrayLength.d.ts +1 -1
- package/dist/{form → platforms/react}/hooks/useFormControl.d.ts +3 -3
- package/dist/{form → platforms/react}/hooks/useFormControlValue.d.ts +2 -2
- package/dist/platforms/react/index.d.ts +23 -0
- package/dist/signals.d.ts +1 -1
- package/dist/strategy-DA3JwxsR.js +170 -0
- package/dist/validation.d.ts +1 -1
- package/dist/validation.js +10 -165
- package/dist/validators/email.d.ts +1 -1
- package/dist/validators/future-date.d.ts +1 -1
- package/dist/validators/integer.d.ts +1 -1
- package/dist/validators/is-date.d.ts +1 -1
- package/dist/validators/is-number.d.ts +1 -1
- package/dist/validators/max-age.d.ts +1 -1
- package/dist/validators/max-date.d.ts +1 -1
- package/dist/validators/max-length.d.ts +1 -1
- package/dist/validators/max.d.ts +1 -1
- package/dist/validators/min-age.d.ts +1 -1
- package/dist/validators/min-date.d.ts +1 -1
- package/dist/validators/min-length.d.ts +1 -1
- package/dist/validators/min.d.ts +1 -1
- package/dist/validators/multiple-of.d.ts +1 -1
- package/dist/validators/non-negative.d.ts +1 -1
- package/dist/validators/non-zero.d.ts +1 -1
- package/dist/validators/past-date.d.ts +1 -1
- package/dist/validators/pattern.d.ts +1 -1
- package/dist/validators/phone.d.ts +1 -1
- package/dist/validators/required.d.ts +1 -1
- package/dist/validators/url.d.ts +1 -1
- package/dist/validators.d.ts +1 -1
- package/llms.txt +64 -43
- package/package.json +4 -4
- package/dist/form/behaviors.d.ts +0 -133
- package/dist/form/validation-schema.d.ts +0 -106
- package/dist/form/validators.d.ts +0 -11
- package/dist/form-model-DhW2HZOB.js +0 -323
- package/dist/state.d.ts +0 -2
- package/dist/state.js +0 -21
- package/dist/subscription-manager-705bls97.js +0 -171
- /package/dist/{state → form/nodes}/subscription-manager.d.ts +0 -0
- /package/dist/{state → model}/behaviors-value.d.ts +0 -0
- /package/dist/{state → model}/derived-registry.d.ts +0 -0
- /package/dist/{state → model}/types.d.ts +0 -0
- /package/dist/{behaviors-node-DGEFMTQu.js → node-DGEFMTQu.js} +0 -0
- /package/dist/{form → platforms/react}/hooks/useSignalSubscription.d.ts +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reformer/core",
|
|
3
|
-
"version": "11.0.0-beta.
|
|
3
|
+
"version": "11.0.0-beta.4",
|
|
4
4
|
"description": "Reactive form state management library for React with signals-based architecture",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
"types": "./dist/signals.d.ts",
|
|
16
16
|
"import": "./dist/signals.js"
|
|
17
17
|
},
|
|
18
|
-
"./
|
|
19
|
-
"types": "./dist/
|
|
20
|
-
"import": "./dist/
|
|
18
|
+
"./model": {
|
|
19
|
+
"types": "./dist/model.d.ts",
|
|
20
|
+
"import": "./dist/model.js"
|
|
21
21
|
},
|
|
22
22
|
"./behaviors": {
|
|
23
23
|
"types": "./dist/behaviors.d.ts",
|
package/dist/form/behaviors.d.ts
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import { Signal, ReadonlySignal } from '@preact/signals-core';
|
|
2
|
-
import { BehaviorCleanup, FormModel, FormProxy } from '../index';
|
|
3
|
-
/** Контекст схемы поведения: модель (значения/сигналы) + форма (ноды). */
|
|
4
|
-
export type BehaviorScope<T> = {
|
|
5
|
-
model: FormModel<T>;
|
|
6
|
-
form: FormProxy<T>;
|
|
7
|
-
};
|
|
8
|
-
/** Результат {@link defineFormBehavior}; передаётся в `createForm({ behavior })`. */
|
|
9
|
-
export interface FormBehavior<T> {
|
|
10
|
-
/** @internal Запускается `createForm` после построения нод и заполнения реестра сигнал→нода. */
|
|
11
|
-
__run(model: FormModel<T>, form: FormProxy<T>): BehaviorCleanup;
|
|
12
|
-
}
|
|
13
|
-
export type { Signal, ReadonlySignal, BehaviorCleanup };
|
|
14
|
-
/** Зарегистрировать произвольную отписку в активной схеме. */
|
|
15
|
-
export declare function onDispose(cleanup: BehaviorCleanup): void;
|
|
16
|
-
/** Текущий scope ({ model, form }) активной схемы (escape hatch для кросс-операторов). */
|
|
17
|
-
export declare function getScope<T>(): BehaviorScope<T>;
|
|
18
|
-
/**
|
|
19
|
-
* Описать поведение формы декларативно. Возвращает {@link FormBehavior} для `createForm({ behavior })`.
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* ```ts
|
|
23
|
-
* export const myBehavior = defineFormBehavior<MyForm>(({ model, form }) => {
|
|
24
|
-
* compute(model.$.total, () => model.price * model.qty);
|
|
25
|
-
* enableWhen([model.$.city], () => Boolean(model.country));
|
|
26
|
-
* onChange(model.$.country, async (c) => form.city.updateComponentProps({ options: await load(c) }));
|
|
27
|
-
* });
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
|
-
export declare function defineFormBehavior<T>(setup: (scope: BehaviorScope<T>) => void): FormBehavior<T>;
|
|
31
|
-
/** Реактивный эффект (авто-dispose). Колбэк может вернуть собственный cleanup. */
|
|
32
|
-
export declare function effect(fn: () => void | (() => void)): void;
|
|
33
|
-
/** Отложенная запись вне effect-контекста (микротаск) — защита от «Cycle detected». */
|
|
34
|
-
export declare function defer(fn: () => void): void;
|
|
35
|
-
/** Вычисляемое поле с auto-tracking: `target = read()` при изменении прочитанных сигналов. */
|
|
36
|
-
export declare function compute<R>(target: Signal<R>, read: () => R, options?: {
|
|
37
|
-
when?: () => boolean;
|
|
38
|
-
}): void;
|
|
39
|
-
/** Вычисляемое поле с явным списком зависимостей (escape hatch). */
|
|
40
|
-
export declare function computeFrom<R>(sources: ReadonlySignal<unknown>[], target: Signal<R>, fn: (...values: any[]) => R, options?: {
|
|
41
|
-
when?: (...values: any[]) => boolean;
|
|
42
|
-
}): void;
|
|
43
|
-
/** Копирование `source → target` — скаляр или группа (объект целиком). */
|
|
44
|
-
export declare function copyFrom<T>(source: ReadonlySignal<T> | object, target: Signal<T> | object, options?: {
|
|
45
|
-
when?: () => boolean;
|
|
46
|
-
transform?: (value: T) => T;
|
|
47
|
-
}): void;
|
|
48
|
-
/** Контекст async-реакции: `signal` аннулируется, когда поле меняется снова до завершения колбэка. */
|
|
49
|
-
export interface ChangeContext {
|
|
50
|
-
signal: AbortSignal;
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Реакция на изменение поля; `{ debounce, immediate }`.
|
|
54
|
-
*
|
|
55
|
-
* Колбэк выполняется ВНЕ effect-контекста (микротаск/таймер) — можно безопасно писать сигналы и ноды
|
|
56
|
-
* (`updateComponentProps`/`reset`/`clear`) без ручного `defer` и без «Cycle detected».
|
|
57
|
-
*
|
|
58
|
-
* Для async-колбэков 2-м аргументом приходит `{ signal }` (AbortSignal): при следующей смене значения
|
|
59
|
-
* предыдущий `signal` аннулируется. Передавай его в `fetch` (сетевая отмена) или проверяй
|
|
60
|
-
* `signal.aborted` перед применением результата — это убирает гонки устаревших ответов (F2).
|
|
61
|
-
*/
|
|
62
|
-
export declare function onChange<T>(source: ReadonlySignal<T>, cb: (value: T, ctx: ChangeContext) => void, options?: {
|
|
63
|
-
immediate?: boolean;
|
|
64
|
-
debounce?: number;
|
|
65
|
-
}): void;
|
|
66
|
-
type EnableTarget = ReadonlySignal<unknown> | object;
|
|
67
|
-
/** Условное включение поля(ей) — скаляр, массив целей или группа. `resetOnDisable=false` по умолчанию. */
|
|
68
|
-
export declare function enableWhen(target: EnableTarget | EnableTarget[], condition: () => boolean, options?: {
|
|
69
|
-
resetOnDisable?: boolean;
|
|
70
|
-
}): void;
|
|
71
|
-
/** Условное выключение поля(ей) (инверсия {@link enableWhen}). */
|
|
72
|
-
export declare function disableWhen(target: EnableTarget | EnableTarget[], condition: () => boolean, options?: {
|
|
73
|
-
resetOnDisable?: boolean;
|
|
74
|
-
}): void;
|
|
75
|
-
/** Трансформация значения поля (идемпотентная). */
|
|
76
|
-
export declare function transformValue<T>(target: Signal<T>, transformer: (value: T) => T): void;
|
|
77
|
-
/** Сброс значения по условию. */
|
|
78
|
-
export declare function resetWhen<T>(target: Signal<T>, condition: () => boolean, options?: {
|
|
79
|
-
resetValue?: T;
|
|
80
|
-
}): void;
|
|
81
|
-
/** Двусторонняя синхронизация двух полей. */
|
|
82
|
-
export declare function syncFields<T>(a: Signal<T>, b: Signal<T>, options?: {
|
|
83
|
-
transform?: (value: T) => T;
|
|
84
|
-
}): void;
|
|
85
|
-
/** Ревалидация при изменении зависимостей. */
|
|
86
|
-
export declare function revalidateWhen(deps: ReadonlySignal<unknown>[], revalidate: () => void): void;
|
|
87
|
-
/**
|
|
88
|
-
* Применить под-схему к КАЖДОМУ элементу динамического массива (per-item поведение).
|
|
89
|
-
* Реагирует на добавление/удаление строк: новым строкам поведение применяется, удалённым — отписывается.
|
|
90
|
-
*
|
|
91
|
-
* Под-схема получает scope строки: `model` (под-модель строки — `row.$.field`) и `form` (нода строки).
|
|
92
|
-
* - Value-операции (`compute`/`copyFrom`/`transformValue` на `row.$.*`) работают всегда.
|
|
93
|
-
* - Node-операции (`enableWhen`/`updateComponentProps`/`reset` через `form.*`) требуют, чтобы массив был
|
|
94
|
-
* МАТЕРИАЛИЗОВАН в форме (узел `{ array, item }` в схеме) — тогда `form` строки = та же нода, что
|
|
95
|
-
* рендерится, а её сигналы зарегистрированы (`enableWhen` резолвит ноду). Без материализации доступ
|
|
96
|
-
* к `form.*` бросит понятную ошибку (см. {@link unmaterializedRowForm}).
|
|
97
|
-
*
|
|
98
|
-
* @example
|
|
99
|
-
* applyEach(model.$.items, defineFormBehavior<Item>(({ model: row, form }) => {
|
|
100
|
-
* compute(row.$.lineTotal, () => row.qty * row.price); // value-op — всегда
|
|
101
|
-
* enableWhen(row.$.discount, () => row.qty > 10); // node-op — нужна материализация массива
|
|
102
|
-
* }));
|
|
103
|
-
*/
|
|
104
|
-
export declare function applyEach<TItem>(array: object, itemSchema: FormBehavior<TItem>): void;
|
|
105
|
-
/**
|
|
106
|
-
* Взаимное исключение булева флага среди строк массива (single-selection — «единственный primary»).
|
|
107
|
-
* Когда флаг строки становится true, у остальных строк он сбрасывается в false. Не хрупок к push:
|
|
108
|
-
* новые строки с флагом false исключения не запускают.
|
|
109
|
-
*
|
|
110
|
-
* @example
|
|
111
|
-
* exclusiveFlag(model.$.contacts, (row) => row.$.primary);
|
|
112
|
-
*/
|
|
113
|
-
export declare function exclusiveFlag<TItem>(array: object, getFlag: (row: FormModel<TItem>) => Signal<boolean>): void;
|
|
114
|
-
/**
|
|
115
|
-
* Агрегатная запись в строки массива. `derive(snapshot)` получает СНИМОК строк и возвращает список
|
|
116
|
-
* `{ index, patch }`, который применяется к строкам. Записи КОАЛЕСИРУЮТСЯ в один отложенный проход на
|
|
117
|
-
* финальном состоянии — поэтому массовые синхронные мутации (push в цикле) не вызывают каскад на
|
|
118
|
-
* промежуточных состояниях. `derive` должна сходиться (на фикспоинте возвращать те же значения).
|
|
119
|
-
*
|
|
120
|
-
* @example
|
|
121
|
-
* // последняя строка = 100 − Σ(остальные)
|
|
122
|
-
* aggregateInto(model.$.rows, (rows) => {
|
|
123
|
-
* const n = rows.length; if (n === 0) return [];
|
|
124
|
-
* const others = rows.slice(0, n - 1).reduce((s, r) => s + r.percent, 0);
|
|
125
|
-
* return [{ index: n - 1, patch: { percent: 100 - others } }];
|
|
126
|
-
* });
|
|
127
|
-
*/
|
|
128
|
-
export declare function aggregateInto<TItem>(array: object, derive: (rows: TItem[]) => Array<{
|
|
129
|
-
index: number;
|
|
130
|
-
patch: Partial<TItem>;
|
|
131
|
-
}>): void;
|
|
132
|
-
/** Применить под-схему к одному или нескольким полям-группам (переиспользование). */
|
|
133
|
-
export declare function apply<TField>(targets: object | object[], subSchema: FormBehavior<TField>): void;
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import { FormModel, ModelArray, PathAwareSignal, ValidationError } from '../index';
|
|
2
|
-
/**
|
|
3
|
-
* Синхронное правило поля типа `TField`. Проверяется ТОЛЬКО значение (`value`).
|
|
4
|
-
*
|
|
5
|
-
* Позиционные `scope`/`root` помечены `never` намеренно: так встроенные value-only фабрики
|
|
6
|
-
* (`required()`/`min()`/… — они `(value, model, root) => …`) и inline-правила `(value) => …`
|
|
7
|
-
* ОБА присваиваются в `Rule<TField>[]` без `any`, при этом сохраняется проверка типа поля
|
|
8
|
-
* (`validate(model.$.age, [email()])` подсветится — `email` ждёт `string`, поле `number`).
|
|
9
|
-
* На вызове раннер приводит правило к callable и передаёт `(value, model, model)`.
|
|
10
|
-
*/
|
|
11
|
-
export type Rule<TField> = (value: TField, scope: never, root: never) => ValidationError | null;
|
|
12
|
-
/**
|
|
13
|
-
* Асинхронное правило поля. Получает `AbortSignal` для отмены устаревших ответов (быстрый повторный
|
|
14
|
-
* прогон той же схемы отменяет предыдущий). Сетевой сбой не должен блокировать — ловите и возвращайте `null`.
|
|
15
|
-
*/
|
|
16
|
-
export type AsyncRule<TField> = (value: TField, ctx: {
|
|
17
|
-
signal: AbortSignal;
|
|
18
|
-
}) => Promise<ValidationError | null>;
|
|
19
|
-
/** Схема валидации — обычная функция над (под)моделью. First-class значение (можно `apply`/тестировать/переиспользовать). */
|
|
20
|
-
export type ValidationSchema<T> = (ctx: {
|
|
21
|
-
model: FormModel<T>;
|
|
22
|
-
}) => void;
|
|
23
|
-
/**
|
|
24
|
-
* Синхронные правила поля.
|
|
25
|
-
*
|
|
26
|
-
* @example
|
|
27
|
-
* ```ts
|
|
28
|
-
* validate(model.$.loanAmount, [required({ message: 'Сумма' }), min(50000)]);
|
|
29
|
-
* ```
|
|
30
|
-
*/
|
|
31
|
-
export declare function validate<TField>(sig: PathAwareSignal<TField>, rules: Rule<TField>[]): void;
|
|
32
|
-
/**
|
|
33
|
-
* Асинхронные правила поля (зеркалит движковое разделение `validators` / `asyncValidators`).
|
|
34
|
-
* Раннер дожидается их и прокидывает `AbortSignal` для отмены устаревших ответов.
|
|
35
|
-
*
|
|
36
|
-
* @example
|
|
37
|
-
* ```ts
|
|
38
|
-
* validateAsync(model.$.username, [async (v, { signal }) => {
|
|
39
|
-
* const r = await fetch(`/api/free?u=${v}`, { signal });
|
|
40
|
-
* return (await r.json()).free ? null : { code: 'taken', message: 'Занято' };
|
|
41
|
-
* }]);
|
|
42
|
-
* ```
|
|
43
|
-
*/
|
|
44
|
-
export declare function validateAsync<TField>(sig: PathAwareSignal<TField>, rules: AsyncRule<TField>[]): void;
|
|
45
|
-
/**
|
|
46
|
-
* Условная валидация: правила внутри `cb` активны, только пока `cond()` истинно; иначе их поля
|
|
47
|
-
* ГАСЯТСЯ (пустой bucket → `setErrors([])`). Не трогает включение/сброс поля — это дело поведения (`enableWhen`).
|
|
48
|
-
*/
|
|
49
|
-
export declare function validateWhen(cond: () => boolean, cb: () => void): void;
|
|
50
|
-
/**
|
|
51
|
-
* Cross-field правило: `fn` получает СНАПШОТ модели текущего scope (`model.get()`) и вешает ошибку на `sig`.
|
|
52
|
-
* Для элементов массива / под-моделей захватывайте нужный снапшот в замыкание (`const item = im.get()`),
|
|
53
|
-
* т.к. `fn` всегда получает модель ТЕКУЩЕГО scope (корень прогона), а не под-модель.
|
|
54
|
-
*/
|
|
55
|
-
export declare function cross<TSnapshot>(sig: PathAwareSignal<unknown>, fn: (form: TSnapshot) => ValidationError | null): void;
|
|
56
|
-
/**
|
|
57
|
-
* Применить под-правила к КАЖДОМУ элементу текущего массива модели.
|
|
58
|
-
* `U extends object` — элементы должны быть под-моделями (объектами); для массива примитивов валидируйте лист напрямую.
|
|
59
|
-
*/
|
|
60
|
-
export declare function each<U extends object>(arr: ModelArray<U>, itemFn: (item: FormModel<U>) => void): void;
|
|
61
|
-
/** Композиция под-схем в текущую (над той же моделью scope). Заменяет пошаговую группировку. */
|
|
62
|
-
export declare function apply<T>(...schemas: ValidationSchema<T>[]): void;
|
|
63
|
-
/**
|
|
64
|
-
* Тонкая identity-обёртка для типизации/discoverability (как `defineFormBehavior`). Возвращает схему как есть.
|
|
65
|
-
*
|
|
66
|
-
* @example
|
|
67
|
-
* ```ts
|
|
68
|
-
* export const step1 = defineValidationSchema<LoanForm>(({ model }) => {
|
|
69
|
-
* validate(model.$.loanAmount, [required(), min(50000)]);
|
|
70
|
-
* });
|
|
71
|
-
* ```
|
|
72
|
-
*/
|
|
73
|
-
export declare function defineValidationSchema<T>(schema: ValidationSchema<T>): ValidationSchema<T>;
|
|
74
|
-
/**
|
|
75
|
-
* Провалидировать модель ЛЮБОЙ схемой (шаг или вся форма). Открывает ambient-окно на время
|
|
76
|
-
* синхронного прогона `schema`, дожидается async-правил, разносит ошибки по нодам формы и гасит
|
|
77
|
-
* поля, ставшие валидными. Возвращает `true`, если нет блокирующих ошибок (`severity:'warning'` не блокирует).
|
|
78
|
-
*
|
|
79
|
-
* Гашение — на пару (model, schema): `validateModel(model, step2)` и `validateModel(model, form)` не мешают
|
|
80
|
-
* друг другу. Устаревший прогон (быстрый повторный вызов той же (model, schema)) отменяется через `AbortSignal`
|
|
81
|
-
* и возвращает `false` (**fail-closed** — отменённому результату нельзя доверять для submit).
|
|
82
|
-
*
|
|
83
|
-
* ⚠️ `schema` должна быть СТАБИЛЬНОЙ ссылкой: отмена устаревших прогонов ключится по идентичности `schema`.
|
|
84
|
-
* Инлайн-стрелка (`validateModel(model, ({ model }) => …)`) каждый раз создаёт НОВЫЙ прогон без дедупликации —
|
|
85
|
-
* держите схемы в `const` / `defineValidationSchema`.
|
|
86
|
-
*
|
|
87
|
-
* `options.touch` (§6): по завершении помечает `touched` ИМЕННО поля, которые схема проверяла
|
|
88
|
-
* (не всё поддерево). Ошибки становятся видимыми (`shouldShowError = invalid && (touched||dirty)`)
|
|
89
|
-
* без ручного `form.markAsTouched()`; при пошаговой валидации следующий шаг не показывается
|
|
90
|
-
* «тронутым» до ввода. Валидные проверенные поля тоже метятся — это безвредно (ошибка не покажется).
|
|
91
|
-
*
|
|
92
|
-
* @param model - Модель данных формы.
|
|
93
|
-
* @param schema - Схема валидации (стабильная ссылка).
|
|
94
|
-
* @param options - `{ touch }` — пометить провалидированные поля `touched` для показа ошибок.
|
|
95
|
-
*
|
|
96
|
-
* @example
|
|
97
|
-
* ```ts
|
|
98
|
-
* const ok = await validateModel(model, step2Validation); // один шаг
|
|
99
|
-
* const all = await validateModel(model, formValidation); // вся форма
|
|
100
|
-
* const stepOk = await validateModel(model, step2Validation, { touch: true }); // + показать ошибки шага
|
|
101
|
-
* ```
|
|
102
|
-
*/
|
|
103
|
-
export declare function validateModel<T>(model: FormModel<T>, schema: ValidationSchema<T>, options?: {
|
|
104
|
-
touch?: boolean;
|
|
105
|
-
}): Promise<boolean>;
|
|
106
|
-
export * from './validation-strategy';
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Точка входа сабпата `@reformer/core/validators` — все фабрики правил одним импортом.
|
|
3
|
-
*
|
|
4
|
-
* Гранулярные сабпаты (`@reformer/core/validators/required` и ещё 21) собираются отдельными
|
|
5
|
-
* entry для tree-shaking; этот модуль отдаёт полный набор для случаев, когда правил много.
|
|
6
|
-
*
|
|
7
|
-
* Ранее здесь был ещё и default-экспорт (`validators` из корневого `index`), заведённый как
|
|
8
|
-
* часть бандл-фикса «shared static registry». Реестров, ради которых он существовал, в коде
|
|
9
|
-
* больше нет, чанк-шаринг он не обеспечивал, потребителей не имел — удалён в 7.0.
|
|
10
|
-
*/
|
|
11
|
-
export * from './validation/index';
|
|
@@ -1,323 +0,0 @@
|
|
|
1
|
-
import { batch as k, signal as _, computed as O } from "@preact/signals-core";
|
|
2
|
-
const h = /* @__PURE__ */ new WeakMap();
|
|
3
|
-
function F(e) {
|
|
4
|
-
h.set(e, (h.get(e) ?? 0) + 1);
|
|
5
|
-
}
|
|
6
|
-
function K(e) {
|
|
7
|
-
const t = h.get(e);
|
|
8
|
-
t !== void 0 && (t <= 1 ? h.delete(e) : h.set(e, t - 1));
|
|
9
|
-
}
|
|
10
|
-
function P(e) {
|
|
11
|
-
return h.has(e);
|
|
12
|
-
}
|
|
13
|
-
const p = (e) => !(e === null || typeof e != "object" || Array.isArray(e) || e instanceof Date || typeof Blob < "u" && e instanceof Blob || typeof File < "u" && e instanceof File), c = (e, t) => e === "" ? String(t) : `${e}.${t}`, m = (e) => /^\d+$/.test(e), l = (e) => {
|
|
14
|
-
if (Array.isArray(e)) return e.map(l);
|
|
15
|
-
if (p(e)) {
|
|
16
|
-
const t = {};
|
|
17
|
-
for (const i of Object.keys(e)) t[i] = l(e[i]);
|
|
18
|
-
return t;
|
|
19
|
-
}
|
|
20
|
-
return e;
|
|
21
|
-
}, g = (e, t) => {
|
|
22
|
-
if (e === t) return !0;
|
|
23
|
-
if (Array.isArray(e) && Array.isArray(t))
|
|
24
|
-
return e.length !== t.length ? !1 : e.every((i, r) => g(i, t[r]));
|
|
25
|
-
if (p(e) && p(t)) {
|
|
26
|
-
const i = Object.keys(e), r = Object.keys(t);
|
|
27
|
-
return i.length !== r.length ? !1 : i.every((n) => g(e[n], t[n]));
|
|
28
|
-
}
|
|
29
|
-
return !1;
|
|
30
|
-
};
|
|
31
|
-
class j {
|
|
32
|
-
constructor(t, i) {
|
|
33
|
-
this.path = i;
|
|
34
|
-
const r = _(t);
|
|
35
|
-
r.__path = i, this.signal = r, this.initial = l(t);
|
|
36
|
-
}
|
|
37
|
-
kind = "leaf";
|
|
38
|
-
signal;
|
|
39
|
-
initial;
|
|
40
|
-
rebase(t) {
|
|
41
|
-
this.path = t, this.signal.__path = t;
|
|
42
|
-
}
|
|
43
|
-
/** Реактивное чтение (внутри effect/computed создаёт зависимость). */
|
|
44
|
-
read() {
|
|
45
|
-
return this.signal.value;
|
|
46
|
-
}
|
|
47
|
-
/** Нереактивный снимок. */
|
|
48
|
-
peek() {
|
|
49
|
-
return this.signal.peek();
|
|
50
|
-
}
|
|
51
|
-
set(t) {
|
|
52
|
-
this.signal.value = t;
|
|
53
|
-
}
|
|
54
|
-
resetToInitial() {
|
|
55
|
-
this.signal.value = l(this.initial);
|
|
56
|
-
}
|
|
57
|
-
captureInitial() {
|
|
58
|
-
this.initial = l(this.signal.peek());
|
|
59
|
-
}
|
|
60
|
-
dirty() {
|
|
61
|
-
return !g(this.signal.peek(), this.initial);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
class x {
|
|
65
|
-
constructor(t, i) {
|
|
66
|
-
this.path = i;
|
|
67
|
-
for (const r of Object.keys(t))
|
|
68
|
-
this.children.set(r, d(t[r], c(i, r)));
|
|
69
|
-
}
|
|
70
|
-
kind = "group";
|
|
71
|
-
children = /* @__PURE__ */ new Map();
|
|
72
|
-
rebase(t) {
|
|
73
|
-
this.path = t;
|
|
74
|
-
for (const [i, r] of this.children) r.rebase(c(t, i));
|
|
75
|
-
}
|
|
76
|
-
/** Реактивное чтение поддерева (внутри effect/computed подписывает на все листья группы). */
|
|
77
|
-
read() {
|
|
78
|
-
const t = {};
|
|
79
|
-
for (const [i, r] of this.children) t[i] = r.read();
|
|
80
|
-
return t;
|
|
81
|
-
}
|
|
82
|
-
peek() {
|
|
83
|
-
const t = {};
|
|
84
|
-
for (const [i, r] of this.children) t[i] = r.peek();
|
|
85
|
-
return t;
|
|
86
|
-
}
|
|
87
|
-
set(t) {
|
|
88
|
-
if (t == null || typeof t != "object") return;
|
|
89
|
-
const i = t;
|
|
90
|
-
k(() => {
|
|
91
|
-
for (const [r, n] of this.children)
|
|
92
|
-
r in i && (n.kind === "leaf" && P(n.signal) || n.set(i[r]));
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
resetToInitial() {
|
|
96
|
-
k(() => {
|
|
97
|
-
for (const t of this.children.values()) t.resetToInitial();
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
captureInitial() {
|
|
101
|
-
for (const t of this.children.values()) t.captureInitial();
|
|
102
|
-
}
|
|
103
|
-
dirty() {
|
|
104
|
-
for (const t of this.children.values()) if (t.dirty()) return !0;
|
|
105
|
-
return !1;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
class N {
|
|
109
|
-
constructor(t, i) {
|
|
110
|
-
this.path = i, this.items = _(t.map((r, n) => d(r, c(i, n)))), this.initial = l(t);
|
|
111
|
-
}
|
|
112
|
-
kind = "array";
|
|
113
|
-
items;
|
|
114
|
-
initial;
|
|
115
|
-
rebase(t) {
|
|
116
|
-
this.path = t, this.items.peek().forEach((i, r) => i.rebase(c(t, r)));
|
|
117
|
-
}
|
|
118
|
-
reindex() {
|
|
119
|
-
this.items.peek().forEach((t, i) => t.rebase(c(this.path, i)));
|
|
120
|
-
}
|
|
121
|
-
push(t) {
|
|
122
|
-
const i = this.items.peek();
|
|
123
|
-
this.items.value = [...i, d(t, c(this.path, i.length))];
|
|
124
|
-
}
|
|
125
|
-
insertAt(t, i) {
|
|
126
|
-
const r = [...this.items.peek()];
|
|
127
|
-
r.splice(t, 0, d(i, c(this.path, t))), this.items.value = r, this.reindex();
|
|
128
|
-
}
|
|
129
|
-
removeAt(t) {
|
|
130
|
-
const i = [...this.items.peek()];
|
|
131
|
-
i.splice(t, 1), this.items.value = i, this.reindex();
|
|
132
|
-
}
|
|
133
|
-
move(t, i) {
|
|
134
|
-
const r = [...this.items.peek()], [n] = r.splice(t, 1);
|
|
135
|
-
n && r.splice(i, 0, n), this.items.value = r, this.reindex();
|
|
136
|
-
}
|
|
137
|
-
swap(t, i) {
|
|
138
|
-
const r = [...this.items.peek()];
|
|
139
|
-
t < 0 || i < 0 || t >= r.length || i >= r.length || t === i || ([r[t], r[i]] = [r[i], r[t]], this.items.value = r, this.reindex());
|
|
140
|
-
}
|
|
141
|
-
clear() {
|
|
142
|
-
this.items.value = [];
|
|
143
|
-
}
|
|
144
|
-
/**
|
|
145
|
-
* Реактивное чтение массива. Читаем `items.value` (а не `.peek()`) — внутри effect/computed это
|
|
146
|
-
* подписка на СОСТАВ массива, поэтому push/removeAt/move ретригерят наравне с правкой элемента.
|
|
147
|
-
*/
|
|
148
|
-
read() {
|
|
149
|
-
return this.items.value.map((t) => t.read());
|
|
150
|
-
}
|
|
151
|
-
peek() {
|
|
152
|
-
return this.items.peek().map((t) => t.peek());
|
|
153
|
-
}
|
|
154
|
-
set(t) {
|
|
155
|
-
const i = Array.isArray(t) ? t : [];
|
|
156
|
-
this.items.value = i.map((r, n) => d(r, c(this.path, n)));
|
|
157
|
-
}
|
|
158
|
-
resetToInitial() {
|
|
159
|
-
this.set(l(this.initial));
|
|
160
|
-
}
|
|
161
|
-
captureInitial() {
|
|
162
|
-
this.initial = l(this.peek());
|
|
163
|
-
}
|
|
164
|
-
dirty() {
|
|
165
|
-
return !g(this.peek(), this.initial);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
function d(e, t) {
|
|
169
|
-
return Array.isArray(e) ? new N(e, t) : p(e) ? new x(e, t) : new j(e, t);
|
|
170
|
-
}
|
|
171
|
-
function f(e) {
|
|
172
|
-
if (e)
|
|
173
|
-
return e.kind === "group" ? S(e) : e.kind === "array" ? E(e) : e.read();
|
|
174
|
-
}
|
|
175
|
-
function E(e) {
|
|
176
|
-
const t = {
|
|
177
|
-
get length() {
|
|
178
|
-
return e.items.value.length;
|
|
179
|
-
},
|
|
180
|
-
push: (i) => e.push(i),
|
|
181
|
-
insertAt: (i, r) => e.insertAt(i, r),
|
|
182
|
-
removeAt: (i) => e.removeAt(i),
|
|
183
|
-
move: (i, r) => e.move(i, r),
|
|
184
|
-
swap: (i, r) => e.swap(i, r),
|
|
185
|
-
clear: () => e.clear(),
|
|
186
|
-
at: (i) => f(e.items.value[i]),
|
|
187
|
-
map: (i) => e.items.value.map((r, n) => i(f(r), n)),
|
|
188
|
-
forEach: (i) => e.items.value.forEach((r, n) => i(f(r), n)),
|
|
189
|
-
toArray: () => e.peek(),
|
|
190
|
-
[Symbol.iterator]: function* () {
|
|
191
|
-
const i = e.items.value;
|
|
192
|
-
for (let r = 0; r < i.length; r++) yield f(i[r]);
|
|
193
|
-
}
|
|
194
|
-
};
|
|
195
|
-
return new Proxy(t, {
|
|
196
|
-
get: (i, r, n) => r === "__path" ? e.path : typeof r == "string" && m(r) ? f(e.items.value[Number(r)]) : Reflect.get(i, r, n),
|
|
197
|
-
has: (i, r) => typeof r == "string" && m(r) ? Number(r) < e.items.value.length : Reflect.has(i, r)
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
const A = /* @__PURE__ */ new WeakMap();
|
|
201
|
-
function M(e) {
|
|
202
|
-
const t = O(() => e.read());
|
|
203
|
-
return {
|
|
204
|
-
get value() {
|
|
205
|
-
return t.value;
|
|
206
|
-
},
|
|
207
|
-
peek: () => t.peek(),
|
|
208
|
-
subscribe: (i) => t.subscribe(i),
|
|
209
|
-
valueOf: () => t.value,
|
|
210
|
-
toString: () => String(t.value),
|
|
211
|
-
toJSON: () => t.value,
|
|
212
|
-
brand: t.brand
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
function I(e) {
|
|
216
|
-
if (e.kind === "leaf") return e.signal;
|
|
217
|
-
const t = A.get(e);
|
|
218
|
-
if (t) return t;
|
|
219
|
-
const i = M(e), r = e.kind === "group", n = (o) => r ? e.children.get(o) : m(o) ? e.items.value[Number(o)] : void 0, s = r ? {} : {
|
|
220
|
-
get length() {
|
|
221
|
-
return e.items.value.length;
|
|
222
|
-
}
|
|
223
|
-
}, u = new Proxy(s, {
|
|
224
|
-
get: (o, a, D) => {
|
|
225
|
-
if (a === "__path") return e.path;
|
|
226
|
-
if (a === "__kind") return e.kind;
|
|
227
|
-
if (typeof a != "string") return Reflect.get(i, a, i);
|
|
228
|
-
const v = n(a);
|
|
229
|
-
return v ? I(v) : !r && Reflect.has(o, a) ? Reflect.get(o, a, D) : Reflect.get(i, a, i);
|
|
230
|
-
},
|
|
231
|
-
has: (o, a) => typeof a == "string" && (n(a) !== void 0 || !r && Reflect.has(o, a)),
|
|
232
|
-
ownKeys: () => r ? [...e.children.keys()] : Reflect.ownKeys(s),
|
|
233
|
-
getOwnPropertyDescriptor: (o, a) => typeof a == "string" && n(a) ? { enumerable: !0, configurable: !0 } : r ? void 0 : Reflect.getOwnPropertyDescriptor(o, a)
|
|
234
|
-
});
|
|
235
|
-
return A.set(e, u), u;
|
|
236
|
-
}
|
|
237
|
-
function W(e) {
|
|
238
|
-
if (e == null || typeof e != "object") return !1;
|
|
239
|
-
const t = e.__kind;
|
|
240
|
-
return t === "group" || t === "array";
|
|
241
|
-
}
|
|
242
|
-
function T(e, t) {
|
|
243
|
-
if (!t) return;
|
|
244
|
-
let i = e;
|
|
245
|
-
for (const r of t.split(".")) {
|
|
246
|
-
if (!i) return;
|
|
247
|
-
if (i.kind === "group") i = i.children.get(r);
|
|
248
|
-
else if (i.kind === "array") i = i.items.peek()[Number(r)];
|
|
249
|
-
else return;
|
|
250
|
-
}
|
|
251
|
-
return i && i.kind === "leaf" ? i.signal : void 0;
|
|
252
|
-
}
|
|
253
|
-
const b = /* @__PURE__ */ new Set([
|
|
254
|
-
"$",
|
|
255
|
-
"get",
|
|
256
|
-
"set",
|
|
257
|
-
"patch",
|
|
258
|
-
"isDirty",
|
|
259
|
-
"reset",
|
|
260
|
-
"captureInitial",
|
|
261
|
-
"signalAt"
|
|
262
|
-
]), w = /* @__PURE__ */ new WeakMap(), R = /* @__PURE__ */ new WeakMap();
|
|
263
|
-
function S(e) {
|
|
264
|
-
const t = w.get(e);
|
|
265
|
-
if (t) return t;
|
|
266
|
-
const i = {
|
|
267
|
-
$: I(e),
|
|
268
|
-
get: () => e.peek(),
|
|
269
|
-
set: (n) => e.set(n),
|
|
270
|
-
patch: (n) => e.set(n),
|
|
271
|
-
isDirty: () => e.dirty(),
|
|
272
|
-
reset: () => e.resetToInitial(),
|
|
273
|
-
captureInitial: () => e.captureInitial(),
|
|
274
|
-
signalAt: (n) => T(e, n)
|
|
275
|
-
}, r = new Proxy(
|
|
276
|
-
{},
|
|
277
|
-
{
|
|
278
|
-
get: (n, s) => {
|
|
279
|
-
if (s === "__path") return e.path;
|
|
280
|
-
if (typeof s != "string") return;
|
|
281
|
-
const u = e.children.get(s);
|
|
282
|
-
if (u) return f(u);
|
|
283
|
-
if (b.has(s)) return i[s];
|
|
284
|
-
},
|
|
285
|
-
set: (n, s, u) => {
|
|
286
|
-
if (typeof s != "string") return !1;
|
|
287
|
-
const o = e.children.get(s);
|
|
288
|
-
return o ? (o.set(u), !0) : !1;
|
|
289
|
-
},
|
|
290
|
-
has: (n, s) => typeof s == "string" && (e.children.has(s) || b.has(s)),
|
|
291
|
-
ownKeys: () => [...e.children.keys()],
|
|
292
|
-
getOwnPropertyDescriptor: (n, s) => typeof s == "string" && e.children.has(s) ? { enumerable: !0, configurable: !0 } : void 0
|
|
293
|
-
}
|
|
294
|
-
);
|
|
295
|
-
return w.set(e, r), R.set(r, e), r;
|
|
296
|
-
}
|
|
297
|
-
function y(e, t) {
|
|
298
|
-
if (e.kind === "leaf") {
|
|
299
|
-
t(e.signal);
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
if (e.kind === "group") {
|
|
303
|
-
for (const i of e.children.values()) y(i, t);
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
for (const i of e.items.value) y(i, t);
|
|
307
|
-
}
|
|
308
|
-
function B(e, t) {
|
|
309
|
-
const i = R.get(e);
|
|
310
|
-
i && y(i, t);
|
|
311
|
-
}
|
|
312
|
-
function C(e) {
|
|
313
|
-
const t = new x(e, "");
|
|
314
|
-
return S(t);
|
|
315
|
-
}
|
|
316
|
-
export {
|
|
317
|
-
P as a,
|
|
318
|
-
C as c,
|
|
319
|
-
B as e,
|
|
320
|
-
W as i,
|
|
321
|
-
F as m,
|
|
322
|
-
K as u
|
|
323
|
-
};
|
package/dist/state.d.ts
DELETED
package/dist/state.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { c as s, a as r, i as o, m as i, u as t } from "./form-model-DhW2HZOB.js";
|
|
2
|
-
import { c as n, a as d, r as l, b as m, d as f, e as u, f as p, s as b, t as k, w as v } from "./behaviors-value-C7dSEhCc.js";
|
|
3
|
-
import { S as F } from "./subscription-manager-705bls97.js";
|
|
4
|
-
export {
|
|
5
|
-
F as SubscriptionManager,
|
|
6
|
-
n as computeFrom,
|
|
7
|
-
d as copyFrom,
|
|
8
|
-
s as createModel,
|
|
9
|
-
r as isDerived,
|
|
10
|
-
o as isModelContainerSignal,
|
|
11
|
-
i as markDerived,
|
|
12
|
-
l as resetWhen,
|
|
13
|
-
m as revalidateWhen,
|
|
14
|
-
f as runOutsideEffect,
|
|
15
|
-
u as safeCallback,
|
|
16
|
-
p as safeDebouncedCallback,
|
|
17
|
-
b as syncFields,
|
|
18
|
-
k as transformValue,
|
|
19
|
-
t as unmarkDerived,
|
|
20
|
-
v as watchField
|
|
21
|
-
};
|