@reformer/core 11.0.0-beta.1 → 11.0.0-beta.2
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.js +134 -134
- package/dist/form-model-DhW2HZOB.js +323 -0
- package/dist/index.js +47 -47
- package/dist/state/form-model.d.ts +14 -0
- package/dist/state/index.d.ts +2 -1
- package/dist/state/types.d.ts +39 -11
- package/dist/state.js +18 -18
- package/dist/validation.js +1 -1
- package/docs/llms/19-reading-values.md +30 -1
- package/docs/llms/20-compute-vs-watch.md +9 -0
- package/llms.txt +104 -5
- package/package.json +1 -1
- package/dist/derived-registry-CluZCQ7U.js +0 -16
- package/dist/form-model-DYCzGGKF.js +0 -286
package/dist/state/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Signal } from '@preact/signals-core';
|
|
1
|
+
import { ReadonlySignal, Signal } from '@preact/signals-core';
|
|
2
2
|
/**
|
|
3
3
|
* Сигнал, который знает свой путь в модели (`'personalData.lastName'`).
|
|
4
4
|
* Используется как «ручка поля»: и привязка value в схеме, и идентичность для testId/devtools.
|
|
@@ -81,8 +81,8 @@ export interface ModelArray<U> {
|
|
|
81
81
|
[index: number]: ModelValue<U>;
|
|
82
82
|
}
|
|
83
83
|
/**
|
|
84
|
-
*
|
|
85
|
-
*
|
|
84
|
+
* Карта под-сигналов объекта в дереве `model.$` (без свойств самого узла — их добавляет
|
|
85
|
+
* {@link ModelGroupSignals}).
|
|
86
86
|
*
|
|
87
87
|
* @group Model
|
|
88
88
|
*/
|
|
@@ -90,16 +90,41 @@ export type ModelSignals<T> = {
|
|
|
90
90
|
[K in keyof T]: ModelSignalNode<T[K]>;
|
|
91
91
|
};
|
|
92
92
|
/**
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
* `PathAwareSignal<string>`, а не `ModelSignals<never>`.
|
|
93
|
+
* Свойства контейнерного узла дерева `$`, «поверх» карты детей. Ключи детей вырезаются из
|
|
94
|
+
* {@link ReadonlySignal}: доступ к полю формы всегда выигрывает у одноимённого свойства сигнала
|
|
95
|
+
* (так же ведёт себя рантайм), поэтому `{ value: string }` не порождает конфликта типов.
|
|
97
96
|
* @internal
|
|
98
97
|
*/
|
|
99
|
-
type
|
|
98
|
+
type ContainerSignal<T, TKeys extends PropertyKey> = Omit<ReadonlySignal<T>, TKeys>;
|
|
99
|
+
/**
|
|
100
|
+
* Узел-группа дерева `model.$`: доступ к под-сигналам полей ({@link ModelSignals}) И одновременно
|
|
101
|
+
* {@link ReadonlySignal} агрегированного значения группы — `.value`/`.peek()`/`.subscribe()`.
|
|
102
|
+
*
|
|
103
|
+
* ⚠️ Поле формы с именем `value`/`peek`/`subscribe`/`valueOf`/`toString`/`toJSON`/`brand` затеняет
|
|
104
|
+
* одноимённое свойство сигнала (редкий краевой случай); `subscribe` при этом продолжает работать.
|
|
105
|
+
*
|
|
106
|
+
* @group Model
|
|
107
|
+
*/
|
|
108
|
+
export type ModelGroupSignals<T> = ModelSignals<T> & ContainerSignal<T, keyof T>;
|
|
109
|
+
/**
|
|
110
|
+
* Узел-массив дерева `model.$`: индексируемый доступ к под-сигналам элементов, реактивная `length`
|
|
111
|
+
* и {@link ReadonlySignal} значения массива целиком (реагирует и на правку элемента, и на изменение
|
|
112
|
+
* состава — push/removeAt/move).
|
|
113
|
+
*
|
|
114
|
+
* @group Model
|
|
115
|
+
*/
|
|
116
|
+
export type ModelArraySignals<U, V> = ContainerSignal<V, 'length'> & {
|
|
100
117
|
readonly length: number;
|
|
101
118
|
readonly [index: number]: ModelSignalNode<U>;
|
|
102
|
-
}
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* Узел дерева сигналов `model.$`: массив → {@link ModelArraySignals}, Opaque (Date/File/Blob)
|
|
122
|
+
* и примитив → {@link PathAwareSignal}, объект → {@link ModelGroupSignals}. Для примитивных
|
|
123
|
+
* и Opaque массивов элемент — сам сигнал листа (а не под-дерево), поэтому `model.$.tags[0]` — это
|
|
124
|
+
* `PathAwareSignal<string>`, а не `ModelGroupSignals<never>`.
|
|
125
|
+
* @internal
|
|
126
|
+
*/
|
|
127
|
+
type ModelSignalNode<V> = NonNullable<V> extends ReadonlyArray<infer U> ? ModelArraySignals<U, NonNullable<V>> : NonNullable<V> extends Opaque ? PathAwareSignal<V> : NonNullable<V> extends object ? ModelGroupSignals<NonNullable<V>> : PathAwareSignal<V>;
|
|
103
128
|
/**
|
|
104
129
|
* API уровня модели (доступно на корне, под-моделях вложенных объектов-групп и элементов массива).
|
|
105
130
|
*
|
|
@@ -109,8 +134,11 @@ type ModelSignalNode<V> = NonNullable<V> extends ReadonlyArray<infer U> ? {
|
|
|
109
134
|
* @group Model
|
|
110
135
|
*/
|
|
111
136
|
export interface ModelApi<T> {
|
|
112
|
-
/**
|
|
113
|
-
|
|
137
|
+
/**
|
|
138
|
+
* Escape-hatch к сигналам: `model.$.loanType` → `PathAwareSignal<LoanType>`.
|
|
139
|
+
* Сам узел — {@link ReadonlySignal} модели целиком: `model.$.subscribe(v => …)`.
|
|
140
|
+
*/
|
|
141
|
+
readonly $: ModelGroupSignals<T>;
|
|
114
142
|
/** Снимок значений (без подписки) — для submit. */
|
|
115
143
|
get(): T;
|
|
116
144
|
/**
|
package/dist/state.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import { c as s } from "./form-model-
|
|
2
|
-
import { c as
|
|
3
|
-
import {
|
|
4
|
-
import { S as D } from "./subscription-manager-705bls97.js";
|
|
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";
|
|
5
4
|
export {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
F as SubscriptionManager,
|
|
6
|
+
n as computeFrom,
|
|
7
|
+
d as copyFrom,
|
|
9
8
|
s as createModel,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
m as
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
21
|
};
|
package/dist/validation.js
CHANGED
|
@@ -29,9 +29,38 @@ model.$.email.peek(); // нереактивный снимок
|
|
|
29
29
|
model.$.address.city.value; // сигнал вложенного поля (≡ model.address.$.city у под-модели)
|
|
30
30
|
|
|
31
31
|
// весь объект
|
|
32
|
-
model.get(); // снимок { email, address: { city }, ... } — для submit
|
|
32
|
+
model.get(); // НЕреактивный снимок { email, address: { city }, ... } — для submit
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
+
> ⚠️ `model.get()` и `model.isDirty()` читают через `peek()` — внутри `effect`/`computed` они НЕ
|
|
36
|
+
> создают зависимостей. `computed(() => model.get())` никогда не пересчитается. Для реактивного
|
|
37
|
+
> чтения всего объекта — узел дерева `$` (ниже).
|
|
38
|
+
|
|
39
|
+
### Подписка на группу и на модель целиком
|
|
40
|
+
|
|
41
|
+
Каждый узел дерева `$` — сигнал: лист отдаёт `PathAwareSignal`, а корень, вложенные объекты-группы
|
|
42
|
+
и массивы — `ReadonlySignal` агрегированного значения поддерева. Поэтому подписаться можно на любом
|
|
43
|
+
уровне, а не только на конкретном поле.
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
model.$.subscribe((all) => autosave(all)); // любое изменение модели; all: T целиком
|
|
47
|
+
model.$.address.subscribe((addr) => ...); // только поддерево address
|
|
48
|
+
model.$.address.value; // реактивный снимок группы
|
|
49
|
+
model.$.address.peek(); // нереактивный снимок группы
|
|
50
|
+
model.$.items.subscribe((rows) => ...); // массив: и правка элемента, и push/removeAt/move
|
|
51
|
+
|
|
52
|
+
// узел — обычный ReadonlySignal, поэтому принимается операциями слоя данных
|
|
53
|
+
watchField(model.$.address, (addr) => geocode(addr));
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Дети узла доступны как раньше — `model.$.address.city` по-прежнему сигнал поля. Подписчик вызывается
|
|
57
|
+
сразу с текущим значением (семантика `Signal.subscribe`), а `model.set(...)`/`model.reset()`
|
|
58
|
+
уведомляют один раз, а не по разу на поле.
|
|
59
|
+
|
|
60
|
+
> ⚠️ Доступ к полю выигрывает у свойства сигнала: если в форме есть поле с именем `value`, `peek`,
|
|
61
|
+
> `subscribe`, `valueOf`, `toString`, `toJSON` или `brand`, то `model.$.<группа>.<это имя>` вернёт
|
|
62
|
+
> сигнал поля. `subscribe` при этом продолжает работать.
|
|
63
|
+
|
|
35
64
|
### В behaviors — читаем model напрямую
|
|
36
65
|
|
|
37
66
|
`compute`/`onChange`/условия `when` читают значения из value-модели (`model.field`) —
|
|
@@ -121,6 +121,14 @@ import { watchField } from '@reformer/core';
|
|
|
121
121
|
const stop = watchField(model.$.country, () => { model.city = ''; });
|
|
122
122
|
```
|
|
123
123
|
|
|
124
|
+
Источником может быть не только поле: узлы-контейнеры дерева `$` (корень, группы, массивы) — тоже
|
|
125
|
+
`ReadonlySignal`, поэтому за поддеревом целиком следят без перечисления полей.
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
watchField(model.$.address, (addr) => geocode(addr)); // любое поле address
|
|
129
|
+
watchField(model.$, (all) => autosave(all)); // любое поле формы
|
|
130
|
+
```
|
|
131
|
+
|
|
124
132
|
### Rule of Thumb
|
|
125
133
|
|
|
126
134
|
| Scenario | Use |
|
|
@@ -129,6 +137,7 @@ const stop = watchField(model.$.country, () => { model.city = ''; });
|
|
|
129
137
|
| Производное с явными зависимостями | `computeFrom` |
|
|
130
138
|
| Async-реакция, обновление componentProps | `onChange` (debounce + AbortSignal) |
|
|
131
139
|
| Простая синхронная реакция (примитив) | `watchField` |
|
|
140
|
+
| Реакция на любое изменение группы/формы | `watchField(model.$.<группа>)` или `.subscribe` |
|
|
132
141
|
|
|
133
142
|
### Chained computeds
|
|
134
143
|
|
package/llms.txt
CHANGED
|
@@ -2259,9 +2259,38 @@ model.$.email.peek(); // нереактивный снимок
|
|
|
2259
2259
|
model.$.address.city.value; // сигнал вложенного поля (≡ model.address.$.city у под-модели)
|
|
2260
2260
|
|
|
2261
2261
|
// весь объект
|
|
2262
|
-
model.get(); // снимок { email, address: { city }, ... } — для submit
|
|
2262
|
+
model.get(); // НЕреактивный снимок { email, address: { city }, ... } — для submit
|
|
2263
2263
|
```
|
|
2264
2264
|
|
|
2265
|
+
> ⚠️ `model.get()` и `model.isDirty()` читают через `peek()` — внутри `effect`/`computed` они НЕ
|
|
2266
|
+
> создают зависимостей. `computed(() => model.get())` никогда не пересчитается. Для реактивного
|
|
2267
|
+
> чтения всего объекта — узел дерева `$` (ниже).
|
|
2268
|
+
|
|
2269
|
+
### Подписка на группу и на модель целиком
|
|
2270
|
+
|
|
2271
|
+
Каждый узел дерева `$` — сигнал: лист отдаёт `PathAwareSignal`, а корень, вложенные объекты-группы
|
|
2272
|
+
и массивы — `ReadonlySignal` агрегированного значения поддерева. Поэтому подписаться можно на любом
|
|
2273
|
+
уровне, а не только на конкретном поле.
|
|
2274
|
+
|
|
2275
|
+
```typescript
|
|
2276
|
+
model.$.subscribe((all) => autosave(all)); // любое изменение модели; all: T целиком
|
|
2277
|
+
model.$.address.subscribe((addr) => ...); // только поддерево address
|
|
2278
|
+
model.$.address.value; // реактивный снимок группы
|
|
2279
|
+
model.$.address.peek(); // нереактивный снимок группы
|
|
2280
|
+
model.$.items.subscribe((rows) => ...); // массив: и правка элемента, и push/removeAt/move
|
|
2281
|
+
|
|
2282
|
+
// узел — обычный ReadonlySignal, поэтому принимается операциями слоя данных
|
|
2283
|
+
watchField(model.$.address, (addr) => geocode(addr));
|
|
2284
|
+
```
|
|
2285
|
+
|
|
2286
|
+
Дети узла доступны как раньше — `model.$.address.city` по-прежнему сигнал поля. Подписчик вызывается
|
|
2287
|
+
сразу с текущим значением (семантика `Signal.subscribe`), а `model.set(...)`/`model.reset()`
|
|
2288
|
+
уведомляют один раз, а не по разу на поле.
|
|
2289
|
+
|
|
2290
|
+
> ⚠️ Доступ к полю выигрывает у свойства сигнала: если в форме есть поле с именем `value`, `peek`,
|
|
2291
|
+
> `subscribe`, `valueOf`, `toString`, `toJSON` или `brand`, то `model.$.<группа>.<это имя>` вернёт
|
|
2292
|
+
> сигнал поля. `subscribe` при этом продолжает работать.
|
|
2293
|
+
|
|
2265
2294
|
### В behaviors — читаем model напрямую
|
|
2266
2295
|
|
|
2267
2296
|
`compute`/`onChange`/условия `when` читают значения из value-модели (`model.field`) —
|
|
@@ -2411,6 +2440,14 @@ import { watchField } from '@reformer/core';
|
|
|
2411
2440
|
const stop = watchField(model.$.country, () => { model.city = ''; });
|
|
2412
2441
|
```
|
|
2413
2442
|
|
|
2443
|
+
Источником может быть не только поле: узлы-контейнеры дерева `$` (корень, группы, массивы) — тоже
|
|
2444
|
+
`ReadonlySignal`, поэтому за поддеревом целиком следят без перечисления полей.
|
|
2445
|
+
|
|
2446
|
+
```typescript
|
|
2447
|
+
watchField(model.$.address, (addr) => geocode(addr)); // любое поле address
|
|
2448
|
+
watchField(model.$, (all) => autosave(all)); // любое поле формы
|
|
2449
|
+
```
|
|
2450
|
+
|
|
2414
2451
|
### Rule of Thumb
|
|
2415
2452
|
|
|
2416
2453
|
| Scenario | Use |
|
|
@@ -2419,6 +2456,7 @@ const stop = watchField(model.$.country, () => { model.city = ''; });
|
|
|
2419
2456
|
| Производное с явными зависимостями | `computeFrom` |
|
|
2420
2457
|
| Async-реакция, обновление componentProps | `onChange` (debounce + AbortSignal) |
|
|
2421
2458
|
| Простая синхронная реакция (примитив) | `watchField` |
|
|
2459
|
+
| Реакция на любое изменение группы/формы | `watchField(model.$.<группа>)` или `.subscribe` |
|
|
2422
2460
|
|
|
2423
2461
|
### Chained computeds
|
|
2424
2462
|
|
|
@@ -6077,6 +6115,29 @@ if (isGroupNode(node)) {
|
|
|
6077
6115
|
|
|
6078
6116
|
_Source: src/form/type-guards.ts_
|
|
6079
6117
|
|
|
6118
|
+
### isModelContainerSignal
|
|
6119
|
+
|
|
6120
|
+
**Kind:** `function`
|
|
6121
|
+
|
|
6122
|
+
Узел дерева `model.$` — контейнер (группа/массив), а не лист?
|
|
6123
|
+
|
|
6124
|
+
Контейнерный узел структурно совместим с `ReadonlySignal` (`peek`/`value`/`subscribe`), поэтому
|
|
6125
|
+
duck-typing «есть `peek` ⇒ это лист» на нём даёт ложное срабатывание. Этот guard — надёжный
|
|
6126
|
+
способ различить: обходчикам дерева нужно спускаться в контейнер, а не читать его целиком.
|
|
6127
|
+
|
|
6128
|
+
**Signature:**
|
|
6129
|
+
```typescript
|
|
6130
|
+
export function isModelContainerSignal(value: unknown): boolean
|
|
6131
|
+
```
|
|
6132
|
+
|
|
6133
|
+
**Examples:**
|
|
6134
|
+
|
|
6135
|
+
```typescript
|
|
6136
|
+
const isLeaf = (v: unknown) => isSignalLike(v) && !isModelContainerSignal(v);
|
|
6137
|
+
```
|
|
6138
|
+
|
|
6139
|
+
_Source: src/state/form-model.ts_
|
|
6140
|
+
|
|
6080
6141
|
### markDerived
|
|
6081
6142
|
|
|
6082
6143
|
**Kind:** `function`
|
|
@@ -6125,8 +6186,11 @@ API уровня модели (доступно на корне, под-моде
|
|
|
6125
6186
|
**Signature:**
|
|
6126
6187
|
```typescript
|
|
6127
6188
|
export interface ModelApi<T> {
|
|
6128
|
-
/**
|
|
6129
|
-
|
|
6189
|
+
/**
|
|
6190
|
+
* Escape-hatch к сигналам: `model.$.loanType` → `PathAwareSignal<LoanType>`.
|
|
6191
|
+
* Сам узел — {@link ReadonlySignal} модели целиком: `model.$.subscribe(v => …)`.
|
|
6192
|
+
*/
|
|
6193
|
+
readonly $: ModelGroupSignals<T>;
|
|
6130
6194
|
/** Снимок значений (без подписки) — для submit. */
|
|
6131
6195
|
get(): T;
|
|
6132
6196
|
/**
|
|
@@ -6270,6 +6334,41 @@ rows.length.value; // 1 (реактивная длина)
|
|
|
6270
6334
|
|
|
6271
6335
|
_Source: src/form/nodes/model-array-node.ts_
|
|
6272
6336
|
|
|
6337
|
+
### ModelArraySignals
|
|
6338
|
+
|
|
6339
|
+
**Kind:** `type`
|
|
6340
|
+
|
|
6341
|
+
Узел-массив дерева `model.$`: индексируемый доступ к под-сигналам элементов, реактивная `length`
|
|
6342
|
+
и {@link ReadonlySignal} значения массива целиком (реагирует и на правку элемента, и на изменение
|
|
6343
|
+
состава — push/removeAt/move).
|
|
6344
|
+
|
|
6345
|
+
**Signature:**
|
|
6346
|
+
```typescript
|
|
6347
|
+
export type ModelArraySignals<U, V> = ContainerSignal<V, 'length'> & {
|
|
6348
|
+
readonly length: number;
|
|
6349
|
+
readonly [index: number]: ModelSignalNode<U>;
|
|
6350
|
+
};
|
|
6351
|
+
```
|
|
6352
|
+
|
|
6353
|
+
_Source: src/state/types.ts_
|
|
6354
|
+
|
|
6355
|
+
### ModelGroupSignals
|
|
6356
|
+
|
|
6357
|
+
**Kind:** `type`
|
|
6358
|
+
|
|
6359
|
+
Узел-группа дерева `model.$`: доступ к под-сигналам полей ({@link ModelSignals}) И одновременно
|
|
6360
|
+
{@link ReadonlySignal} агрегированного значения группы — `.value`/`.peek()`/`.subscribe()`.
|
|
6361
|
+
|
|
6362
|
+
⚠️ Поле формы с именем `value`/`peek`/`subscribe`/`valueOf`/`toString`/`toJSON`/`brand` затеняет
|
|
6363
|
+
одноимённое свойство сигнала (редкий краевой случай); `subscribe` при этом продолжает работать.
|
|
6364
|
+
|
|
6365
|
+
**Signature:**
|
|
6366
|
+
```typescript
|
|
6367
|
+
export type ModelGroupSignals<T> = ModelSignals<T> & ContainerSignal<T, keyof T>;
|
|
6368
|
+
```
|
|
6369
|
+
|
|
6370
|
+
_Source: src/state/types.ts_
|
|
6371
|
+
|
|
6273
6372
|
### ModelObject
|
|
6274
6373
|
|
|
6275
6374
|
**Kind:** `type`
|
|
@@ -6291,8 +6390,8 @@ _Source: src/state/types.ts_
|
|
|
6291
6390
|
|
|
6292
6391
|
**Kind:** `type`
|
|
6293
6392
|
|
|
6294
|
-
|
|
6295
|
-
|
|
6393
|
+
Карта под-сигналов объекта в дереве `model.$` (без свойств самого узла — их добавляет
|
|
6394
|
+
{@link ModelGroupSignals}).
|
|
6296
6395
|
|
|
6297
6396
|
**Signature:**
|
|
6298
6397
|
```typescript
|
package/package.json
CHANGED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
const t = /* @__PURE__ */ new WeakMap();
|
|
2
|
-
function n(e) {
|
|
3
|
-
t.set(e, (t.get(e) ?? 0) + 1);
|
|
4
|
-
}
|
|
5
|
-
function i(e) {
|
|
6
|
-
const r = t.get(e);
|
|
7
|
-
r !== void 0 && (r <= 1 ? t.delete(e) : t.set(e, r - 1));
|
|
8
|
-
}
|
|
9
|
-
function o(e) {
|
|
10
|
-
return t.has(e);
|
|
11
|
-
}
|
|
12
|
-
export {
|
|
13
|
-
o as i,
|
|
14
|
-
n as m,
|
|
15
|
-
i as u
|
|
16
|
-
};
|
|
@@ -1,286 +0,0 @@
|
|
|
1
|
-
import { signal as k } from "@preact/signals-core";
|
|
2
|
-
import { i as b } from "./derived-registry-CluZCQ7U.js";
|
|
3
|
-
const h = (e) => !(e === null || typeof e != "object" || Array.isArray(e) || e instanceof Date || typeof Blob < "u" && e instanceof Blob || typeof File < "u" && e instanceof File), a = (e, i) => e === "" ? String(i) : `${e}.${i}`, p = (e) => /^\d+$/.test(e), o = (e) => {
|
|
4
|
-
if (Array.isArray(e)) return e.map(o);
|
|
5
|
-
if (h(e)) {
|
|
6
|
-
const i = {};
|
|
7
|
-
for (const t of Object.keys(e)) i[t] = o(e[t]);
|
|
8
|
-
return i;
|
|
9
|
-
}
|
|
10
|
-
return e;
|
|
11
|
-
}, u = (e, i) => {
|
|
12
|
-
if (e === i) return !0;
|
|
13
|
-
if (Array.isArray(e) && Array.isArray(i))
|
|
14
|
-
return e.length !== i.length ? !1 : e.every((t, r) => u(t, i[r]));
|
|
15
|
-
if (h(e) && h(i)) {
|
|
16
|
-
const t = Object.keys(e), r = Object.keys(i);
|
|
17
|
-
return t.length !== r.length ? !1 : t.every((n) => u(e[n], i[n]));
|
|
18
|
-
}
|
|
19
|
-
return !1;
|
|
20
|
-
};
|
|
21
|
-
class x {
|
|
22
|
-
constructor(i, t) {
|
|
23
|
-
this.path = t;
|
|
24
|
-
const r = k(i);
|
|
25
|
-
r.__path = t, this.signal = r, this.initial = o(i);
|
|
26
|
-
}
|
|
27
|
-
kind = "leaf";
|
|
28
|
-
signal;
|
|
29
|
-
initial;
|
|
30
|
-
rebase(i) {
|
|
31
|
-
this.path = i, this.signal.__path = i;
|
|
32
|
-
}
|
|
33
|
-
/** Реактивное чтение (внутри effect/computed создаёт зависимость). */
|
|
34
|
-
read() {
|
|
35
|
-
return this.signal.value;
|
|
36
|
-
}
|
|
37
|
-
/** Нереактивный снимок. */
|
|
38
|
-
peek() {
|
|
39
|
-
return this.signal.peek();
|
|
40
|
-
}
|
|
41
|
-
set(i) {
|
|
42
|
-
this.signal.value = i;
|
|
43
|
-
}
|
|
44
|
-
resetToInitial() {
|
|
45
|
-
this.signal.value = o(this.initial);
|
|
46
|
-
}
|
|
47
|
-
captureInitial() {
|
|
48
|
-
this.initial = o(this.signal.peek());
|
|
49
|
-
}
|
|
50
|
-
dirty() {
|
|
51
|
-
return !u(this.signal.peek(), this.initial);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
class A {
|
|
55
|
-
constructor(i, t) {
|
|
56
|
-
this.path = t;
|
|
57
|
-
for (const r of Object.keys(i))
|
|
58
|
-
this.children.set(r, c(i[r], a(t, r)));
|
|
59
|
-
}
|
|
60
|
-
kind = "group";
|
|
61
|
-
children = /* @__PURE__ */ new Map();
|
|
62
|
-
rebase(i) {
|
|
63
|
-
this.path = i;
|
|
64
|
-
for (const [t, r] of this.children) r.rebase(a(i, t));
|
|
65
|
-
}
|
|
66
|
-
peek() {
|
|
67
|
-
const i = {};
|
|
68
|
-
for (const [t, r] of this.children) i[t] = r.peek();
|
|
69
|
-
return i;
|
|
70
|
-
}
|
|
71
|
-
set(i) {
|
|
72
|
-
if (i == null || typeof i != "object") return;
|
|
73
|
-
const t = i;
|
|
74
|
-
for (const [r, n] of this.children)
|
|
75
|
-
r in t && (n.kind === "leaf" && b(n.signal) || n.set(t[r]));
|
|
76
|
-
}
|
|
77
|
-
resetToInitial() {
|
|
78
|
-
for (const i of this.children.values()) i.resetToInitial();
|
|
79
|
-
}
|
|
80
|
-
captureInitial() {
|
|
81
|
-
for (const i of this.children.values()) i.captureInitial();
|
|
82
|
-
}
|
|
83
|
-
dirty() {
|
|
84
|
-
for (const i of this.children.values()) if (i.dirty()) return !0;
|
|
85
|
-
return !1;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
class I {
|
|
89
|
-
constructor(i, t) {
|
|
90
|
-
this.path = t, this.items = k(i.map((r, n) => c(r, a(t, n)))), this.initial = o(i);
|
|
91
|
-
}
|
|
92
|
-
kind = "array";
|
|
93
|
-
items;
|
|
94
|
-
initial;
|
|
95
|
-
rebase(i) {
|
|
96
|
-
this.path = i, this.items.peek().forEach((t, r) => t.rebase(a(i, r)));
|
|
97
|
-
}
|
|
98
|
-
reindex() {
|
|
99
|
-
this.items.peek().forEach((i, t) => i.rebase(a(this.path, t)));
|
|
100
|
-
}
|
|
101
|
-
push(i) {
|
|
102
|
-
const t = this.items.peek();
|
|
103
|
-
this.items.value = [...t, c(i, a(this.path, t.length))];
|
|
104
|
-
}
|
|
105
|
-
insertAt(i, t) {
|
|
106
|
-
const r = [...this.items.peek()];
|
|
107
|
-
r.splice(i, 0, c(t, a(this.path, i))), this.items.value = r, this.reindex();
|
|
108
|
-
}
|
|
109
|
-
removeAt(i) {
|
|
110
|
-
const t = [...this.items.peek()];
|
|
111
|
-
t.splice(i, 1), this.items.value = t, this.reindex();
|
|
112
|
-
}
|
|
113
|
-
move(i, t) {
|
|
114
|
-
const r = [...this.items.peek()], [n] = r.splice(i, 1);
|
|
115
|
-
n && r.splice(t, 0, n), this.items.value = r, this.reindex();
|
|
116
|
-
}
|
|
117
|
-
swap(i, t) {
|
|
118
|
-
const r = [...this.items.peek()];
|
|
119
|
-
i < 0 || t < 0 || i >= r.length || t >= r.length || i === t || ([r[i], r[t]] = [r[t], r[i]], this.items.value = r, this.reindex());
|
|
120
|
-
}
|
|
121
|
-
clear() {
|
|
122
|
-
this.items.value = [];
|
|
123
|
-
}
|
|
124
|
-
peek() {
|
|
125
|
-
return this.items.peek().map((i) => i.peek());
|
|
126
|
-
}
|
|
127
|
-
set(i) {
|
|
128
|
-
const t = Array.isArray(i) ? i : [];
|
|
129
|
-
this.items.value = t.map((r, n) => c(r, a(this.path, n)));
|
|
130
|
-
}
|
|
131
|
-
resetToInitial() {
|
|
132
|
-
this.set(o(this.initial));
|
|
133
|
-
}
|
|
134
|
-
captureInitial() {
|
|
135
|
-
this.initial = o(this.peek());
|
|
136
|
-
}
|
|
137
|
-
dirty() {
|
|
138
|
-
return !u(this.peek(), this.initial);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
function c(e, i) {
|
|
142
|
-
return Array.isArray(e) ? new I(e, i) : h(e) ? new A(e, i) : new x(e, i);
|
|
143
|
-
}
|
|
144
|
-
function l(e) {
|
|
145
|
-
if (e)
|
|
146
|
-
return e.kind === "group" ? _(e) : e.kind === "array" ? P(e) : e.read();
|
|
147
|
-
}
|
|
148
|
-
function P(e) {
|
|
149
|
-
const i = {
|
|
150
|
-
get length() {
|
|
151
|
-
return e.items.value.length;
|
|
152
|
-
},
|
|
153
|
-
push: (t) => e.push(t),
|
|
154
|
-
insertAt: (t, r) => e.insertAt(t, r),
|
|
155
|
-
removeAt: (t) => e.removeAt(t),
|
|
156
|
-
move: (t, r) => e.move(t, r),
|
|
157
|
-
swap: (t, r) => e.swap(t, r),
|
|
158
|
-
clear: () => e.clear(),
|
|
159
|
-
at: (t) => l(e.items.value[t]),
|
|
160
|
-
map: (t) => e.items.value.map((r, n) => t(l(r), n)),
|
|
161
|
-
forEach: (t) => e.items.value.forEach((r, n) => t(l(r), n)),
|
|
162
|
-
toArray: () => e.peek(),
|
|
163
|
-
[Symbol.iterator]: function* () {
|
|
164
|
-
const t = e.items.value;
|
|
165
|
-
for (let r = 0; r < t.length; r++) yield l(t[r]);
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
return new Proxy(i, {
|
|
169
|
-
get: (t, r, n) => r === "__path" ? e.path : typeof r == "string" && p(r) ? l(e.items.value[Number(r)]) : Reflect.get(t, r, n),
|
|
170
|
-
has: (t, r) => typeof r == "string" && p(r) ? Number(r) < e.items.value.length : Reflect.has(t, r)
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
function d(e) {
|
|
174
|
-
if (e.kind === "leaf") return e.signal;
|
|
175
|
-
if (e.kind === "group")
|
|
176
|
-
return new Proxy(
|
|
177
|
-
{},
|
|
178
|
-
{
|
|
179
|
-
get: (t, r) => {
|
|
180
|
-
if (r === "__path") return e.path;
|
|
181
|
-
if (typeof r != "string") return;
|
|
182
|
-
const n = e.children.get(r);
|
|
183
|
-
return n ? d(n) : void 0;
|
|
184
|
-
},
|
|
185
|
-
has: (t, r) => typeof r == "string" && e.children.has(r),
|
|
186
|
-
ownKeys: () => [...e.children.keys()],
|
|
187
|
-
getOwnPropertyDescriptor: (t, r) => typeof r == "string" && e.children.has(r) ? { enumerable: !0, configurable: !0 } : void 0
|
|
188
|
-
}
|
|
189
|
-
);
|
|
190
|
-
const i = e;
|
|
191
|
-
return new Proxy(
|
|
192
|
-
{
|
|
193
|
-
get length() {
|
|
194
|
-
return i.items.value.length;
|
|
195
|
-
}
|
|
196
|
-
},
|
|
197
|
-
{
|
|
198
|
-
get: (t, r, n) => {
|
|
199
|
-
if (r === "__path") return i.path;
|
|
200
|
-
if (typeof r == "string" && p(r)) {
|
|
201
|
-
const s = i.items.value[Number(r)];
|
|
202
|
-
return s ? d(s) : void 0;
|
|
203
|
-
}
|
|
204
|
-
return Reflect.get(t, r, n);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
);
|
|
208
|
-
}
|
|
209
|
-
function j(e, i) {
|
|
210
|
-
if (!i) return;
|
|
211
|
-
let t = e;
|
|
212
|
-
for (const r of i.split(".")) {
|
|
213
|
-
if (!t) return;
|
|
214
|
-
if (t.kind === "group") t = t.children.get(r);
|
|
215
|
-
else if (t.kind === "array") t = t.items.peek()[Number(r)];
|
|
216
|
-
else return;
|
|
217
|
-
}
|
|
218
|
-
return t && t.kind === "leaf" ? t.signal : void 0;
|
|
219
|
-
}
|
|
220
|
-
const y = /* @__PURE__ */ new Set([
|
|
221
|
-
"$",
|
|
222
|
-
"get",
|
|
223
|
-
"set",
|
|
224
|
-
"patch",
|
|
225
|
-
"isDirty",
|
|
226
|
-
"reset",
|
|
227
|
-
"captureInitial",
|
|
228
|
-
"signalAt"
|
|
229
|
-
]), v = /* @__PURE__ */ new WeakMap(), w = /* @__PURE__ */ new WeakMap();
|
|
230
|
-
function _(e) {
|
|
231
|
-
const i = v.get(e);
|
|
232
|
-
if (i) return i;
|
|
233
|
-
const t = {
|
|
234
|
-
$: d(e),
|
|
235
|
-
get: () => e.peek(),
|
|
236
|
-
set: (n) => e.set(n),
|
|
237
|
-
patch: (n) => e.set(n),
|
|
238
|
-
isDirty: () => e.dirty(),
|
|
239
|
-
reset: () => e.resetToInitial(),
|
|
240
|
-
captureInitial: () => e.captureInitial(),
|
|
241
|
-
signalAt: (n) => j(e, n)
|
|
242
|
-
}, r = new Proxy(
|
|
243
|
-
{},
|
|
244
|
-
{
|
|
245
|
-
get: (n, s) => {
|
|
246
|
-
if (s === "__path") return e.path;
|
|
247
|
-
if (typeof s != "string") return;
|
|
248
|
-
const f = e.children.get(s);
|
|
249
|
-
if (f) return l(f);
|
|
250
|
-
if (y.has(s)) return t[s];
|
|
251
|
-
},
|
|
252
|
-
set: (n, s, f) => {
|
|
253
|
-
if (typeof s != "string") return !1;
|
|
254
|
-
const g = e.children.get(s);
|
|
255
|
-
return g ? (g.set(f), !0) : !1;
|
|
256
|
-
},
|
|
257
|
-
has: (n, s) => typeof s == "string" && (e.children.has(s) || y.has(s)),
|
|
258
|
-
ownKeys: () => [...e.children.keys()],
|
|
259
|
-
getOwnPropertyDescriptor: (n, s) => typeof s == "string" && e.children.has(s) ? { enumerable: !0, configurable: !0 } : void 0
|
|
260
|
-
}
|
|
261
|
-
);
|
|
262
|
-
return v.set(e, r), w.set(r, e), r;
|
|
263
|
-
}
|
|
264
|
-
function m(e, i) {
|
|
265
|
-
if (e.kind === "leaf") {
|
|
266
|
-
i(e.signal);
|
|
267
|
-
return;
|
|
268
|
-
}
|
|
269
|
-
if (e.kind === "group") {
|
|
270
|
-
for (const t of e.children.values()) m(t, i);
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
|
-
for (const t of e.items.value) m(t, i);
|
|
274
|
-
}
|
|
275
|
-
function D(e, i) {
|
|
276
|
-
const t = w.get(e);
|
|
277
|
-
t && m(t, i);
|
|
278
|
-
}
|
|
279
|
-
function O(e) {
|
|
280
|
-
const i = new A(e, "");
|
|
281
|
-
return _(i);
|
|
282
|
-
}
|
|
283
|
-
export {
|
|
284
|
-
O as c,
|
|
285
|
-
D as e
|
|
286
|
-
};
|