@reformer/core 11.0.0-beta.7 → 11.0.0-beta.8
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 +7 -3
- package/dist/behaviors.js +211 -174
- package/dist/create-model-BBRHhHwK.js +30 -0
- package/dist/form/behaviors/collections.d.ts +14 -1
- package/dist/form/behaviors/context.d.ts +18 -2
- package/dist/form/behaviors/index.d.ts +3 -1
- package/dist/form/behaviors/internals.d.ts +0 -1
- package/dist/form/behaviors/schema-operators.d.ts +76 -0
- package/dist/form/behaviors/types.d.ts +15 -3
- package/dist/form/create-core-form.d.ts +5 -3
- package/dist/form/create-form.d.ts +28 -53
- package/dist/form/form-bundle.d.ts +136 -0
- package/dist/form/index.d.ts +6 -1
- package/dist/form/nodes/field-node.d.ts +2 -2
- package/dist/form/nodes/group-node.d.ts +1 -1
- package/dist/form/nodes/model-array-node.d.ts +10 -5
- package/dist/form/schema-controller.d.ts +137 -0
- package/dist/form/schema-subtree.d.ts +28 -0
- package/dist/form/signal-node-registry.d.ts +22 -16
- package/dist/form/types/deep-schema.d.ts +2 -2
- package/dist/form/types/form-proxy.d.ts +1 -1
- package/dist/form/types/schema-node.d.ts +37 -12
- package/dist/form/validation/context.d.ts +6 -1
- package/dist/form/validation/index.d.ts +1 -1
- package/dist/form/validation/operators.d.ts +70 -8
- package/dist/form/validation/types.d.ts +2 -1
- package/dist/index.js +345 -254
- package/dist/model/index.d.ts +5 -0
- package/dist/model/model-nodes.d.ts +67 -4
- package/dist/model/model-signals-proxy.d.ts +8 -1
- package/dist/model/model-value-proxy.d.ts +76 -0
- package/dist/model/types.d.ts +7 -4
- package/dist/model-value-proxy-caTmAiUd.js +419 -0
- package/dist/model.js +19 -17
- package/dist/node-BeV_lV5i.js +100 -0
- package/dist/platforms/react/form-bundle-context.d.ts +27 -0
- package/dist/platforms/react/index.d.ts +1 -0
- package/dist/strategy-DgzELWno.js +204 -0
- package/dist/validation.js +7 -6
- package/docs/llms/03-api-signatures.md +4 -4
- package/docs/llms/09-formschema.md +5 -5
- package/docs/llms/10-arrays.md +23 -4
- package/docs/llms/16-ui-components.md +2 -2
- package/docs/llms/18-conditional-fields.md +1 -1
- package/docs/llms/21-array-operations.md +26 -0
- package/docs/llms/28-submit-and-reset.md +3 -3
- package/docs/llms/29-async-preload.md +4 -4
- package/docs/llms/33-validation-strategy.md +2 -2
- package/docs/llms/35-unified-contract.md +246 -0
- package/llms-index.json +1 -1
- package/llms.txt +1148 -122
- package/package.json +1 -1
- package/dist/create-model-B8U-COv-.js +0 -103
- package/dist/model-signals-proxy-2vqS243J.js +0 -250
- package/dist/node-DGEFMTQu.js +0 -18
- package/dist/strategy-BXS_gBfz.js +0 -170
package/dist/model/index.d.ts
CHANGED
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
* Состав (реэкспорты — из тех же файлов, что использует зонтик `.`, поэтому `.` и `/model` — один
|
|
14
14
|
* module-инстанс: общий `derived`-WeakMap, единая идентичность `Signal`):
|
|
15
15
|
* - **модель** — `createModel` + типы `FormModel`/`ModelArray`/… + `PathAwareSignal`;
|
|
16
|
+
* - **шаблон элемента массива** — `arrayOf(blank)`: что кладёт `push()` без значения;
|
|
16
17
|
* - **обход значений** — `eachLeafSignal` (листья) и `eachValueSignal` (листья и массивы целиком);
|
|
18
|
+
* - **ручка → фасад** — `modelOf(model.$.<группа | массив>)`: под-модель или фасад массива;
|
|
17
19
|
* - **value-операции** (реактивные правила) — `computeFrom`/`copyFrom`/`watchField`/`transformValue`/
|
|
18
20
|
* `resetWhen`/`syncFields`/`revalidateWhen`;
|
|
19
21
|
* - **producer-owned флаг** — `markDerived`/`isDerived`/`unmarkDerived`;
|
|
@@ -23,8 +25,11 @@
|
|
|
23
25
|
* @module model
|
|
24
26
|
*/
|
|
25
27
|
export { createModel, eachLeafSignal, eachValueSignal } from './create-model';
|
|
28
|
+
export { arrayOf } from './model-nodes';
|
|
26
29
|
export type { FormModel, ModelArray, ModelArraySignals, ModelGroupSignals, ModelObject, ModelValue, ModelSignals, ModelApi, PathAwareSignal, } from './types';
|
|
27
30
|
export { isModelContainerSignal, isValueSignal } from './model-signals-proxy';
|
|
31
|
+
export { modelOf } from './model-value-proxy';
|
|
32
|
+
export type { ModelOf } from './model-value-proxy';
|
|
28
33
|
export { computeFrom, copyFrom, watchField, transformValue, resetWhen, syncFields, revalidateWhen, } from './behaviors-value';
|
|
29
34
|
export type { BehaviorCleanup } from './behaviors-value';
|
|
30
35
|
export { markDerived, isDerived, unmarkDerived } from './derived-registry';
|
|
@@ -1,6 +1,50 @@
|
|
|
1
1
|
import { Signal } from '@preact/signals-core';
|
|
2
2
|
import { PathAwareSignal } from './types';
|
|
3
3
|
export declare const isIndexKey: (key: string) => boolean;
|
|
4
|
+
/** Фабрика нового элемента массива. */
|
|
5
|
+
type Blank = () => unknown;
|
|
6
|
+
/**
|
|
7
|
+
* Образец того же места модели внутри шаблона родительского массива. Ленивый: шаблон родителя
|
|
8
|
+
* ради него вызывается, только когда образец действительно понадобился.
|
|
9
|
+
*/
|
|
10
|
+
type Shape = () => unknown;
|
|
11
|
+
/**
|
|
12
|
+
* Массив модели с шаблоном нового элемента.
|
|
13
|
+
*
|
|
14
|
+
* Шаблон — фабрика значения, которое получает `push()` / `insertAt(i)` без аргумента: кнопка
|
|
15
|
+
* «Добавить» секции массива, `model.items.push()` в поведении. Объявляется там же, где остальные
|
|
16
|
+
* начальные значения, — в модели; схеме и JSX знать его не нужно.
|
|
17
|
+
*
|
|
18
|
+
* Возвращает обычный массив: тип поля остаётся `U[]`. Шаблон переживает `set` / `patch` / `reset`
|
|
19
|
+
* модели. Вложенные массивы объявляются внутри шаблона элемента — тогда и элементы, пришедшие
|
|
20
|
+
* обычными данными (загрузка с сервера), получают их шаблоны.
|
|
21
|
+
*
|
|
22
|
+
* Шаблон привязан к самому массиву, поэтому до `createModel` его нельзя копировать:
|
|
23
|
+
* `structuredClone`, `JSON.parse(JSON.stringify(…))` и `[...array]` отдают массив без шаблона, и
|
|
24
|
+
* `push()` без значения бросит ошибку. Начальные значения с `arrayOf` держат в фабрике
|
|
25
|
+
* (`const initial = () => ({ items: arrayOf(blank) })`), а не в константе, которую клонируют.
|
|
26
|
+
*
|
|
27
|
+
* @typeParam U - Тип элемента массива.
|
|
28
|
+
* @param blank - Фабрика нового элемента. Вызывается на каждое добавление — значение не делится
|
|
29
|
+
* между элементами.
|
|
30
|
+
* @param items - Начальные элементы. По умолчанию массив пуст.
|
|
31
|
+
* @returns Массив начальных значений с шаблоном.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* const blankPhone = () => ({ number: '' });
|
|
36
|
+
* const blankCoBorrower = () => ({ name: '', phones: arrayOf(blankPhone) });
|
|
37
|
+
*
|
|
38
|
+
* const model = createModel({ coBorrowers: arrayOf(blankCoBorrower) });
|
|
39
|
+
*
|
|
40
|
+
* model.coBorrowers.push(); // { name: '', phones: [] }
|
|
41
|
+
* model.coBorrowers.at(0).phones.push(); // { number: '' }
|
|
42
|
+
* model.coBorrowers.push({ name: 'Анна', phones: [] }); // значение целиком — как раньше
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @group Model
|
|
46
|
+
*/
|
|
47
|
+
export declare function arrayOf<U>(blank: () => U, items?: readonly U[]): U[];
|
|
4
48
|
export type ModelNode = LeafNode | GroupNode | ArrayNode;
|
|
5
49
|
declare class LeafNode {
|
|
6
50
|
path: string;
|
|
@@ -22,7 +66,11 @@ export declare class GroupNode {
|
|
|
22
66
|
path: string;
|
|
23
67
|
readonly kind: "group";
|
|
24
68
|
readonly children: Map<string, ModelNode>;
|
|
25
|
-
|
|
69
|
+
/**
|
|
70
|
+
* @param shape Образец той же группы из шаблона родительского массива: из него вложенные
|
|
71
|
+
* массивы берут шаблоны, если значение пришло обычными данными (см. {@link ArrayNode}).
|
|
72
|
+
*/
|
|
73
|
+
constructor(initial: Record<string, unknown>, path: string, shape?: Shape);
|
|
26
74
|
rebase(path: string): void;
|
|
27
75
|
/** Реактивное чтение поддерева (внутри effect/computed подписывает на все листья группы). */
|
|
28
76
|
read(): Record<string, unknown>;
|
|
@@ -34,14 +82,27 @@ export declare class GroupNode {
|
|
|
34
82
|
}
|
|
35
83
|
export declare class ArrayNode {
|
|
36
84
|
path: string;
|
|
85
|
+
private readonly shape?;
|
|
37
86
|
readonly kind: "array";
|
|
38
87
|
readonly items: Signal<ModelNode[]>;
|
|
39
88
|
private initial;
|
|
40
|
-
|
|
89
|
+
private blank;
|
|
90
|
+
private fallback;
|
|
91
|
+
private sample;
|
|
92
|
+
/** @param shape Образец того же массива из шаблона родителя (см. {@link GroupNode}). */
|
|
93
|
+
constructor(initial: unknown[], path: string, shape?: Shape | undefined);
|
|
94
|
+
/** Шаблон по старшинству: свой (`arrayOf`) → из шаблона родителя → из схемы. */
|
|
95
|
+
private template;
|
|
96
|
+
private readonly itemShape;
|
|
97
|
+
private build;
|
|
98
|
+
/** Значение нового элемента: переданное либо из шаблона. */
|
|
99
|
+
private fresh;
|
|
100
|
+
/** Шаблон из схемы (`initialValue` узла-массива) — запасной: шаблон модели главнее. */
|
|
101
|
+
provideBlank(blank: Blank): void;
|
|
41
102
|
rebase(path: string): void;
|
|
42
103
|
private reindex;
|
|
43
|
-
push(value
|
|
44
|
-
insertAt(index: number, value
|
|
104
|
+
push(value?: unknown): void;
|
|
105
|
+
insertAt(index: number, value?: unknown): void;
|
|
45
106
|
removeAt(index: number): void;
|
|
46
107
|
move(from: number, to: number): void;
|
|
47
108
|
swap(a: number, b: number): void;
|
|
@@ -57,4 +118,6 @@ export declare class ArrayNode {
|
|
|
57
118
|
captureInitial(): void;
|
|
58
119
|
dirty(): boolean;
|
|
59
120
|
}
|
|
121
|
+
/** Значение `initialValue` узла-массива схемы как шаблон: фабрика — как есть, значение — копией. */
|
|
122
|
+
export declare const blankFrom: (initialValue: unknown) => Blank;
|
|
60
123
|
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Signal } from '@preact/signals-core';
|
|
2
2
|
import { PathAwareSignal } from './types';
|
|
3
|
-
import { ModelNode, GroupNode } from './model-nodes';
|
|
3
|
+
import { ModelNode, GroupNode, ArrayNode } from './model-nodes';
|
|
4
4
|
/**
|
|
5
5
|
* Узел дерева `$`: лист → сам {@link PathAwareSignal}, группа/массив → контейнерный узел —
|
|
6
6
|
* {@link ReadonlySignal} агрегированного значения ПЛЮС доступ к детям по имени/индексу.
|
|
@@ -20,6 +20,13 @@ import { ModelNode, GroupNode } from './model-nodes';
|
|
|
20
20
|
* полей, а потребители, перечисляющие модель, не видят служебных ключей.
|
|
21
21
|
*/
|
|
22
22
|
export declare function signalsProxy(node: ModelNode): any;
|
|
23
|
+
/**
|
|
24
|
+
* Узел модели за контейнерной ручкой дерева `$` (группа или массив). Для листа и для всего, что
|
|
25
|
+
* ручкой не является, — `undefined`.
|
|
26
|
+
*
|
|
27
|
+
* @internal
|
|
28
|
+
*/
|
|
29
|
+
export declare function containerNodeOf(handle: unknown): GroupNode | ArrayNode | undefined;
|
|
23
30
|
/**
|
|
24
31
|
* Узел дерева `model.$` — контейнер (группа/массив), а не лист?
|
|
25
32
|
*
|
|
@@ -1,3 +1,79 @@
|
|
|
1
1
|
import { GroupNode } from './model-nodes';
|
|
2
|
+
import { FormModel, ModelArray } from './types';
|
|
2
3
|
export declare const rootByFacade: WeakMap<object, GroupNode>;
|
|
3
4
|
export declare function makeFormModel(group: GroupNode): any;
|
|
5
|
+
/**
|
|
6
|
+
* Value-фасад для ручки {@link modelOf}: массив → {@link ModelArray}, объект → под-модель
|
|
7
|
+
* {@link FormModel}.
|
|
8
|
+
*
|
|
9
|
+
* @group Model
|
|
10
|
+
*/
|
|
11
|
+
export type ModelOf<V> = NonNullable<V> extends ReadonlyArray<infer U> ? ModelArray<U> : FormModel<NonNullable<V>>;
|
|
12
|
+
/**
|
|
13
|
+
* Value-фасад по ручке дерева `model.$`: группа → под-модель {@link FormModel}, массив →
|
|
14
|
+
* {@link ModelArray}. Обратная операция к `model.$`: `modelOf(model.$.address) === model.address`.
|
|
15
|
+
*
|
|
16
|
+
* Ручка находит фасад по ИДЕНТИЧНОСТИ, а не по пути. Путь для этого не годится: он абсолютный
|
|
17
|
+
* (`items.0.phones`) и меняется при перестановке строк, а области поведения и формы строк
|
|
18
|
+
* вложенные — поиск по пути от под-модели строки ничего не находит.
|
|
19
|
+
*
|
|
20
|
+
* Лист ручкой контейнера не является — функция бросает. В частности, группа или массив,
|
|
21
|
+
* созданные из начального `null`, в модели — лист: вид узла фиксируется при создании.
|
|
22
|
+
*
|
|
23
|
+
* @typeParam V - Значение, которое хранит ручка (выводится из `peek()`).
|
|
24
|
+
* @param handle - Узел-группа или узел-массив дерева `model.$`.
|
|
25
|
+
* @returns Под-модель группы либо фасад массива.
|
|
26
|
+
* @throws TypeError если `handle` — лист или не ручка модели.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* const model = createModel({ address: { city: '' }, phones: [{ number: '' }] });
|
|
31
|
+
*
|
|
32
|
+
* modelOf(model.$.address).city = 'Казань'; // под-модель: чтение, запись, get/set/patch
|
|
33
|
+
* modelOf(model.$.phones).push({ number: '+7' }); // фасад массива: мутации и обход
|
|
34
|
+
* modelOf(model.$.phones[0]).number; // строка массива — тоже группа
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @group Model
|
|
38
|
+
*/
|
|
39
|
+
export declare function modelOf<V extends object | null | undefined>(handle: {
|
|
40
|
+
peek(): V;
|
|
41
|
+
}): ModelOf<V>;
|
|
42
|
+
/**
|
|
43
|
+
* Ручка `$` по value-фасаду: под-модель → её `model.$`, фасад массива → узел-массив дерева `$`.
|
|
44
|
+
* Для всего остального — `undefined`.
|
|
45
|
+
*
|
|
46
|
+
* @internal
|
|
47
|
+
*/
|
|
48
|
+
export declare function signalsOf(facade: unknown): object | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* Ручка массива по привязке: сама ручка `model.$.<массив>` либо value-фасад `model.<массив>`.
|
|
51
|
+
* Для всего остального — `undefined`.
|
|
52
|
+
*
|
|
53
|
+
* @internal
|
|
54
|
+
*/
|
|
55
|
+
export declare function arrayHandleOf(binding: unknown): object | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* Ручка группы по привязке: `model.$.<группа>` либо под-модель `model.<группа>`. Для всего
|
|
58
|
+
* остального — `undefined`.
|
|
59
|
+
*
|
|
60
|
+
* @internal
|
|
61
|
+
*/
|
|
62
|
+
export declare function groupHandleOf(binding: unknown): object | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* Запасной шаблон нового элемента массива — `initialValue` узла-массива схемы. Действует, только
|
|
65
|
+
* если модель своего шаблона не объявила (`arrayOf`): так живут формы, чья модель строится из
|
|
66
|
+
* данных без кода.
|
|
67
|
+
*
|
|
68
|
+
* @internal
|
|
69
|
+
*/
|
|
70
|
+
export declare function provideArrayBlank(handle: unknown, initialValue: unknown): void;
|
|
71
|
+
/**
|
|
72
|
+
* Value-фасад модели (под-модель или фасад массива), а не обычный объект?
|
|
73
|
+
*
|
|
74
|
+
* Обходчикам чужих деревьев (схема формы) в фасад спускаться нельзя: чтение его ключей — это
|
|
75
|
+
* реактивное чтение значений модели.
|
|
76
|
+
*
|
|
77
|
+
* @internal
|
|
78
|
+
*/
|
|
79
|
+
export declare function isModelFacade(value: unknown): boolean;
|
package/dist/model/types.d.ts
CHANGED
|
@@ -57,10 +57,13 @@ export interface ModelArray<U> {
|
|
|
57
57
|
readonly __path: string;
|
|
58
58
|
/** Реактивная длина. */
|
|
59
59
|
readonly length: number;
|
|
60
|
-
/**
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Добавить элемент в конец (значение элемента целиком). Без значения — элемент по шаблону
|
|
62
|
+
* массива (`arrayOf(blank)` в модели); шаблона нет — ошибка.
|
|
63
|
+
*/
|
|
64
|
+
push(item?: U): void;
|
|
65
|
+
/** Вставить элемент по индексу. Без значения — элемент по шаблону, как у {@link push}. */
|
|
66
|
+
insertAt(index: number, item?: U): void;
|
|
64
67
|
/** Удалить элемент по индексу. */
|
|
65
68
|
removeAt(index: number): void;
|
|
66
69
|
/** Переместить элемент. */
|
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
import { batch as A, signal as x, Signal as I, computed as F } from "@preact/signals-core";
|
|
2
|
+
const u = /* @__PURE__ */ new WeakMap();
|
|
3
|
+
function z(e) {
|
|
4
|
+
u.set(e, (u.get(e) ?? 0) + 1);
|
|
5
|
+
}
|
|
6
|
+
function Q(e) {
|
|
7
|
+
const t = u.get(e);
|
|
8
|
+
t !== void 0 && (t <= 1 ? u.delete(e) : u.set(e, t - 1));
|
|
9
|
+
}
|
|
10
|
+
function T(e) {
|
|
11
|
+
return u.has(e);
|
|
12
|
+
}
|
|
13
|
+
const d = (e) => !(e === null || typeof e != "object" || Array.isArray(e) || e instanceof Date || typeof Blob < "u" && e instanceof Blob || typeof File < "u" && e instanceof File), h = (e, t) => e === "" ? String(t) : `${e}.${t}`, v = (e) => /^\d+$/.test(e), c = (e) => {
|
|
14
|
+
if (Array.isArray(e)) return e.map(c);
|
|
15
|
+
if (d(e)) {
|
|
16
|
+
const t = {};
|
|
17
|
+
for (const r of Object.keys(e)) t[r] = c(e[r]);
|
|
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((r, i) => g(r, t[i]));
|
|
25
|
+
if (d(e) && d(t)) {
|
|
26
|
+
const r = Object.keys(e), i = Object.keys(t);
|
|
27
|
+
return r.length !== i.length ? !1 : r.every((n) => g(e[n], t[n]));
|
|
28
|
+
}
|
|
29
|
+
return !1;
|
|
30
|
+
}, M = Symbol.for("@reformer/core:arrayBlank"), _ = (e) => Array.isArray(e) ? e[M] : void 0, W = (e, t) => d(e) ? e[t] : void 0;
|
|
31
|
+
function U(e, t = []) {
|
|
32
|
+
const r = [...t];
|
|
33
|
+
return Object.defineProperty(r, M, { value: e }), r;
|
|
34
|
+
}
|
|
35
|
+
class C {
|
|
36
|
+
constructor(t, r) {
|
|
37
|
+
this.path = r;
|
|
38
|
+
const i = x(t);
|
|
39
|
+
i.__path = r, this.signal = i, this.initial = c(t);
|
|
40
|
+
}
|
|
41
|
+
kind = "leaf";
|
|
42
|
+
signal;
|
|
43
|
+
initial;
|
|
44
|
+
rebase(t) {
|
|
45
|
+
this.path = t, this.signal.__path = t;
|
|
46
|
+
}
|
|
47
|
+
/** Реактивное чтение (внутри effect/computed создаёт зависимость). */
|
|
48
|
+
read() {
|
|
49
|
+
return this.signal.value;
|
|
50
|
+
}
|
|
51
|
+
/** Нереактивный снимок. */
|
|
52
|
+
peek() {
|
|
53
|
+
return this.signal.peek();
|
|
54
|
+
}
|
|
55
|
+
set(t) {
|
|
56
|
+
this.signal.value = t;
|
|
57
|
+
}
|
|
58
|
+
resetToInitial() {
|
|
59
|
+
this.signal.value = c(this.initial);
|
|
60
|
+
}
|
|
61
|
+
captureInitial() {
|
|
62
|
+
this.initial = c(this.signal.peek());
|
|
63
|
+
}
|
|
64
|
+
dirty() {
|
|
65
|
+
return !g(this.signal.peek(), this.initial);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
class K {
|
|
69
|
+
/**
|
|
70
|
+
* @param shape Образец той же группы из шаблона родительского массива: из него вложенные
|
|
71
|
+
* массивы берут шаблоны, если значение пришло обычными данными (см. {@link ArrayNode}).
|
|
72
|
+
*/
|
|
73
|
+
constructor(t, r, i) {
|
|
74
|
+
this.path = r;
|
|
75
|
+
for (const n of Object.keys(t)) {
|
|
76
|
+
const s = i && (() => W(i(), n));
|
|
77
|
+
this.children.set(n, E(t[n], h(r, n), s));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
kind = "group";
|
|
81
|
+
children = /* @__PURE__ */ new Map();
|
|
82
|
+
rebase(t) {
|
|
83
|
+
this.path = t;
|
|
84
|
+
for (const [r, i] of this.children) i.rebase(h(t, r));
|
|
85
|
+
}
|
|
86
|
+
/** Реактивное чтение поддерева (внутри effect/computed подписывает на все листья группы). */
|
|
87
|
+
read() {
|
|
88
|
+
const t = {};
|
|
89
|
+
for (const [r, i] of this.children) t[r] = i.read();
|
|
90
|
+
return t;
|
|
91
|
+
}
|
|
92
|
+
peek() {
|
|
93
|
+
const t = {};
|
|
94
|
+
for (const [r, i] of this.children) t[r] = i.peek();
|
|
95
|
+
return t;
|
|
96
|
+
}
|
|
97
|
+
set(t) {
|
|
98
|
+
if (t == null || typeof t != "object") return;
|
|
99
|
+
const r = t;
|
|
100
|
+
A(() => {
|
|
101
|
+
for (const [i, n] of this.children)
|
|
102
|
+
i in r && (n.kind === "leaf" && T(n.signal) || n.set(r[i]));
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
resetToInitial() {
|
|
106
|
+
A(() => {
|
|
107
|
+
for (const t of this.children.values()) t.resetToInitial();
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
captureInitial() {
|
|
111
|
+
for (const t of this.children.values()) t.captureInitial();
|
|
112
|
+
}
|
|
113
|
+
dirty() {
|
|
114
|
+
for (const t of this.children.values()) if (t.dirty()) return !0;
|
|
115
|
+
return !1;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
class G {
|
|
119
|
+
/** @param shape Образец того же массива из шаблона родителя (см. {@link GroupNode}). */
|
|
120
|
+
constructor(t, r, i) {
|
|
121
|
+
this.path = r, this.shape = i, this.blank = _(t), this.items = x(t.map((n, s) => this.build(n, s))), this.initial = c(t);
|
|
122
|
+
}
|
|
123
|
+
kind = "array";
|
|
124
|
+
items;
|
|
125
|
+
initial;
|
|
126
|
+
// Шаблон нового элемента (`arrayOf`). Принадлежит узлу, а не значению: `set`/`reset` заменяют
|
|
127
|
+
// элементы, узел остаётся — и шаблон с ним.
|
|
128
|
+
blank;
|
|
129
|
+
// Запасной шаблон — `initialValue` узла-массива схемы.
|
|
130
|
+
fallback;
|
|
131
|
+
// Образец элемента — один вызов шаблона. Нужен только ради вложенных шаблонов: элемент из
|
|
132
|
+
// обычных данных (`set` с сервера, `push(значение)`) метку `arrayOf` на своих массивах не несёт.
|
|
133
|
+
sample;
|
|
134
|
+
/** Шаблон по старшинству: свой (`arrayOf`) → из шаблона родителя → из схемы. */
|
|
135
|
+
template() {
|
|
136
|
+
return this.blank ??= _(this.shape?.()), this.blank ?? this.fallback;
|
|
137
|
+
}
|
|
138
|
+
// Образец читается лениво — лишний вызов шаблона случается, только когда вложенному массиву
|
|
139
|
+
// элемента из обычных данных понадобился свой шаблон.
|
|
140
|
+
itemShape = () => {
|
|
141
|
+
if (!this.sample) {
|
|
142
|
+
const t = this.template();
|
|
143
|
+
if (!t) return;
|
|
144
|
+
this.sample = { value: t() };
|
|
145
|
+
}
|
|
146
|
+
return this.sample.value;
|
|
147
|
+
};
|
|
148
|
+
build(t, r) {
|
|
149
|
+
return E(t, h(this.path, r), this.itemShape);
|
|
150
|
+
}
|
|
151
|
+
/** Значение нового элемента: переданное либо из шаблона. */
|
|
152
|
+
fresh(t) {
|
|
153
|
+
if (t !== void 0) return t;
|
|
154
|
+
const r = this.template();
|
|
155
|
+
if (!r)
|
|
156
|
+
throw new Error(
|
|
157
|
+
`[@reformer/core] ${this.path || "массив"}: добавление элемента без значения, а шаблона у массива нет. Объявите его в модели — \`arrayOf(() => ({ … }))\` — либо передайте значение: \`push(значение)\`.`
|
|
158
|
+
);
|
|
159
|
+
return r();
|
|
160
|
+
}
|
|
161
|
+
/** Шаблон из схемы (`initialValue` узла-массива) — запасной: шаблон модели главнее. */
|
|
162
|
+
provideBlank(t) {
|
|
163
|
+
this.fallback ??= t;
|
|
164
|
+
}
|
|
165
|
+
rebase(t) {
|
|
166
|
+
this.path = t, this.items.peek().forEach((r, i) => r.rebase(h(t, i)));
|
|
167
|
+
}
|
|
168
|
+
reindex() {
|
|
169
|
+
this.items.peek().forEach((t, r) => t.rebase(h(this.path, r)));
|
|
170
|
+
}
|
|
171
|
+
push(t) {
|
|
172
|
+
const r = this.items.peek();
|
|
173
|
+
this.items.value = [...r, this.build(this.fresh(t), r.length)];
|
|
174
|
+
}
|
|
175
|
+
insertAt(t, r) {
|
|
176
|
+
const i = [...this.items.peek()];
|
|
177
|
+
i.splice(t, 0, this.build(this.fresh(r), t)), this.items.value = i, this.reindex();
|
|
178
|
+
}
|
|
179
|
+
removeAt(t) {
|
|
180
|
+
const r = [...this.items.peek()];
|
|
181
|
+
r.splice(t, 1), this.items.value = r, this.reindex();
|
|
182
|
+
}
|
|
183
|
+
move(t, r) {
|
|
184
|
+
const i = [...this.items.peek()], [n] = i.splice(t, 1);
|
|
185
|
+
n && i.splice(r, 0, n), this.items.value = i, this.reindex();
|
|
186
|
+
}
|
|
187
|
+
swap(t, r) {
|
|
188
|
+
const i = [...this.items.peek()];
|
|
189
|
+
t < 0 || r < 0 || t >= i.length || r >= i.length || t === r || ([i[t], i[r]] = [i[r], i[t]], this.items.value = i, this.reindex());
|
|
190
|
+
}
|
|
191
|
+
clear() {
|
|
192
|
+
this.items.value = [];
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Реактивное чтение массива. Читаем `items.value` (а не `.peek()`) — внутри effect/computed это
|
|
196
|
+
* подписка на СОСТАВ массива, поэтому push/removeAt/move ретригерят наравне с правкой элемента.
|
|
197
|
+
*/
|
|
198
|
+
read() {
|
|
199
|
+
return this.items.value.map((t) => t.read());
|
|
200
|
+
}
|
|
201
|
+
peek() {
|
|
202
|
+
return this.items.peek().map((t) => t.peek());
|
|
203
|
+
}
|
|
204
|
+
set(t) {
|
|
205
|
+
const r = Array.isArray(t) ? t : [];
|
|
206
|
+
this.items.value = r.map((i, n) => this.build(i, n));
|
|
207
|
+
}
|
|
208
|
+
resetToInitial() {
|
|
209
|
+
this.set(c(this.initial));
|
|
210
|
+
}
|
|
211
|
+
captureInitial() {
|
|
212
|
+
this.initial = c(this.peek());
|
|
213
|
+
}
|
|
214
|
+
dirty() {
|
|
215
|
+
return !g(this.peek(), this.initial);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function E(e, t, r) {
|
|
219
|
+
return Array.isArray(e) ? new G(e, t, r) : d(e) ? new K(e, t, r) : new C(e, t);
|
|
220
|
+
}
|
|
221
|
+
const H = (e) => typeof e == "function" ? e : () => c(e), O = /* @__PURE__ */ new WeakMap(), N = /* @__PURE__ */ new WeakMap();
|
|
222
|
+
function L(e) {
|
|
223
|
+
const t = F(() => e.read());
|
|
224
|
+
return {
|
|
225
|
+
get value() {
|
|
226
|
+
return t.value;
|
|
227
|
+
},
|
|
228
|
+
peek: () => t.peek(),
|
|
229
|
+
subscribe: (r) => t.subscribe(r),
|
|
230
|
+
valueOf: () => t.value,
|
|
231
|
+
toString: () => String(t.value),
|
|
232
|
+
toJSON: () => t.value,
|
|
233
|
+
brand: t.brand
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
function p(e) {
|
|
237
|
+
if (e.kind === "leaf") return e.signal;
|
|
238
|
+
const t = O.get(e);
|
|
239
|
+
if (t) return t;
|
|
240
|
+
const r = L(e), i = e.kind === "group", n = (l) => i ? e.children.get(l) : v(l) ? e.items.value[Number(l)] : void 0, s = i ? {} : {
|
|
241
|
+
get length() {
|
|
242
|
+
return e.items.value.length;
|
|
243
|
+
}
|
|
244
|
+
}, o = new Proxy(s, {
|
|
245
|
+
get: (l, a, k) => {
|
|
246
|
+
if (a === "__path") return e.path;
|
|
247
|
+
if (a === "__kind") return e.kind;
|
|
248
|
+
if (typeof a != "string") return Reflect.get(r, a, r);
|
|
249
|
+
const m = n(a);
|
|
250
|
+
return m ? p(m) : !i && Reflect.has(l, a) ? Reflect.get(l, a, k) : Reflect.get(r, a, r);
|
|
251
|
+
},
|
|
252
|
+
set: (l, a, k) => {
|
|
253
|
+
if (!i && a === "value")
|
|
254
|
+
return e.set(k), !0;
|
|
255
|
+
const m = e.path === "" ? "model.$" : `model.$.${e.path}`;
|
|
256
|
+
throw new TypeError(
|
|
257
|
+
`[@reformer/core] ${m}: запись в «${String(a)}» ` + (i ? "узла-группы невозможна — группа читается целиком, а пишется по полям (model.$.<поле>.value) либо через model.patch(...)" : "узла-массива невозможна — массив заменяется целиком через .value, а элемент правится своим сигналом (model.$.<массив>[i].value)")
|
|
258
|
+
);
|
|
259
|
+
},
|
|
260
|
+
has: (l, a) => typeof a == "string" && (n(a) !== void 0 || !i && Reflect.has(l, a)),
|
|
261
|
+
ownKeys: () => i ? [...e.children.keys()] : Reflect.ownKeys(s),
|
|
262
|
+
getOwnPropertyDescriptor: (l, a) => typeof a == "string" && n(a) ? { enumerable: !0, configurable: !0 } : i ? void 0 : Reflect.getOwnPropertyDescriptor(l, a)
|
|
263
|
+
});
|
|
264
|
+
return O.set(e, o), N.set(o, e), o;
|
|
265
|
+
}
|
|
266
|
+
function P(e) {
|
|
267
|
+
if (!(e == null || typeof e != "object"))
|
|
268
|
+
return N.get(e);
|
|
269
|
+
}
|
|
270
|
+
function V(e) {
|
|
271
|
+
if (e == null || typeof e != "object") return !1;
|
|
272
|
+
const t = e.__kind;
|
|
273
|
+
return t === "group" || t === "array";
|
|
274
|
+
}
|
|
275
|
+
function y(e) {
|
|
276
|
+
return e == null || typeof e != "object" ? !1 : e.__kind === "array";
|
|
277
|
+
}
|
|
278
|
+
function X(e) {
|
|
279
|
+
return e instanceof I || y(e);
|
|
280
|
+
}
|
|
281
|
+
function q(e, t) {
|
|
282
|
+
if (!t) return;
|
|
283
|
+
let r = e;
|
|
284
|
+
for (const i of t.split(".")) {
|
|
285
|
+
if (!r) return;
|
|
286
|
+
if (r.kind === "group") r = r.children.get(i);
|
|
287
|
+
else if (r.kind === "array") r = r.items.peek()[Number(i)];
|
|
288
|
+
else return;
|
|
289
|
+
}
|
|
290
|
+
if (r)
|
|
291
|
+
return r.kind === "leaf" ? r.signal : r.kind === "array" ? p(r) : void 0;
|
|
292
|
+
}
|
|
293
|
+
function f(e) {
|
|
294
|
+
if (e)
|
|
295
|
+
return e.kind === "group" ? B(e) : e.kind === "array" ? R(e) : e.read();
|
|
296
|
+
}
|
|
297
|
+
const S = /* @__PURE__ */ new WeakMap(), b = /* @__PURE__ */ new WeakMap();
|
|
298
|
+
function R(e) {
|
|
299
|
+
const t = S.get(e);
|
|
300
|
+
if (t) return t;
|
|
301
|
+
const r = {
|
|
302
|
+
get length() {
|
|
303
|
+
return e.items.value.length;
|
|
304
|
+
},
|
|
305
|
+
push: (n) => e.push(n),
|
|
306
|
+
insertAt: (n, s) => e.insertAt(n, s),
|
|
307
|
+
removeAt: (n) => e.removeAt(n),
|
|
308
|
+
move: (n, s) => e.move(n, s),
|
|
309
|
+
swap: (n, s) => e.swap(n, s),
|
|
310
|
+
clear: () => e.clear(),
|
|
311
|
+
at: (n) => f(e.items.value[n]),
|
|
312
|
+
map: (n) => e.items.value.map((s, o) => n(f(s), o)),
|
|
313
|
+
forEach: (n) => e.items.value.forEach((s, o) => n(f(s), o)),
|
|
314
|
+
toArray: () => e.peek(),
|
|
315
|
+
[Symbol.iterator]: function* () {
|
|
316
|
+
const n = e.items.value;
|
|
317
|
+
for (let s = 0; s < n.length; s++) yield f(n[s]);
|
|
318
|
+
}
|
|
319
|
+
}, i = new Proxy(r, {
|
|
320
|
+
get: (n, s, o) => s === "__path" ? e.path : typeof s == "string" && v(s) ? f(e.items.value[Number(s)]) : Reflect.get(n, s, o),
|
|
321
|
+
has: (n, s) => typeof s == "string" && v(s) ? Number(s) < e.items.value.length : Reflect.has(n, s)
|
|
322
|
+
});
|
|
323
|
+
return S.set(e, i), b.set(i, e), i;
|
|
324
|
+
}
|
|
325
|
+
const $ = /* @__PURE__ */ new Set([
|
|
326
|
+
"$",
|
|
327
|
+
"get",
|
|
328
|
+
"set",
|
|
329
|
+
"patch",
|
|
330
|
+
"isDirty",
|
|
331
|
+
"reset",
|
|
332
|
+
"captureInitial",
|
|
333
|
+
"signalAt"
|
|
334
|
+
]), j = /* @__PURE__ */ new WeakMap(), w = /* @__PURE__ */ new WeakMap();
|
|
335
|
+
function B(e) {
|
|
336
|
+
const t = j.get(e);
|
|
337
|
+
if (t) return t;
|
|
338
|
+
const r = {
|
|
339
|
+
$: p(e),
|
|
340
|
+
get: () => e.peek(),
|
|
341
|
+
set: (n) => e.set(n),
|
|
342
|
+
patch: (n) => e.set(n),
|
|
343
|
+
isDirty: () => e.dirty(),
|
|
344
|
+
reset: () => e.resetToInitial(),
|
|
345
|
+
captureInitial: () => e.captureInitial(),
|
|
346
|
+
signalAt: (n) => q(e, n)
|
|
347
|
+
}, i = new Proxy(
|
|
348
|
+
{},
|
|
349
|
+
{
|
|
350
|
+
get: (n, s) => {
|
|
351
|
+
if (s === "__path") return e.path;
|
|
352
|
+
if (typeof s != "string") return;
|
|
353
|
+
const o = e.children.get(s);
|
|
354
|
+
if (o) return f(o);
|
|
355
|
+
if ($.has(s)) return r[s];
|
|
356
|
+
},
|
|
357
|
+
set: (n, s, o) => {
|
|
358
|
+
if (typeof s != "string") return !1;
|
|
359
|
+
const l = e.children.get(s);
|
|
360
|
+
return l ? (l.set(o), !0) : !1;
|
|
361
|
+
},
|
|
362
|
+
has: (n, s) => typeof s == "string" && (e.children.has(s) || $.has(s)),
|
|
363
|
+
ownKeys: () => [...e.children.keys()],
|
|
364
|
+
getOwnPropertyDescriptor: (n, s) => typeof s == "string" && e.children.has(s) ? { enumerable: !0, configurable: !0 } : void 0
|
|
365
|
+
}
|
|
366
|
+
);
|
|
367
|
+
return j.set(e, i), w.set(i, e), i;
|
|
368
|
+
}
|
|
369
|
+
function Y(e) {
|
|
370
|
+
const t = P(e);
|
|
371
|
+
if (!t) {
|
|
372
|
+
const r = e?.__path, i = typeof r == "string" && r !== "" ? ` «${r}»` : "";
|
|
373
|
+
throw new TypeError(
|
|
374
|
+
e instanceof I ? `[@reformer/core] modelOf: ручка${i} — лист, у него нет под-модели. Группа или массив, созданные из начального null, в модели тоже лист: задайте начальное значение объектом или массивом.` : "[@reformer/core] modelOf: ожидалась ручка группы или массива из дерева model.$ (например, model.$.address или model.$.items)."
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
return t.kind === "group" ? B(t) : R(t);
|
|
378
|
+
}
|
|
379
|
+
function D(e) {
|
|
380
|
+
if (e == null || typeof e != "object") return;
|
|
381
|
+
const t = b.get(e);
|
|
382
|
+
if (t) return p(t);
|
|
383
|
+
const r = w.get(e);
|
|
384
|
+
return r ? p(r) : void 0;
|
|
385
|
+
}
|
|
386
|
+
function Z(e) {
|
|
387
|
+
if (y(e)) return e;
|
|
388
|
+
const t = D(e);
|
|
389
|
+
return y(t) ? t : void 0;
|
|
390
|
+
}
|
|
391
|
+
function ee(e) {
|
|
392
|
+
const t = V(e) ? e : D(e);
|
|
393
|
+
return t !== void 0 && !y(t) ? t : void 0;
|
|
394
|
+
}
|
|
395
|
+
function te(e, t) {
|
|
396
|
+
const r = P(e);
|
|
397
|
+
r?.kind === "array" && r.provideBlank(H(t));
|
|
398
|
+
}
|
|
399
|
+
function re(e) {
|
|
400
|
+
return e == null || typeof e != "object" ? !1 : b.has(e) || w.has(e);
|
|
401
|
+
}
|
|
402
|
+
export {
|
|
403
|
+
K as G,
|
|
404
|
+
U as a,
|
|
405
|
+
X as b,
|
|
406
|
+
z as c,
|
|
407
|
+
T as d,
|
|
408
|
+
B as e,
|
|
409
|
+
re as f,
|
|
410
|
+
ee as g,
|
|
411
|
+
Z as h,
|
|
412
|
+
V as i,
|
|
413
|
+
y as j,
|
|
414
|
+
Y as m,
|
|
415
|
+
te as p,
|
|
416
|
+
w as r,
|
|
417
|
+
p as s,
|
|
418
|
+
Q as u
|
|
419
|
+
};
|