r-state-tree 0.4.7 → 0.5.0
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 +87 -0
- package/dist/observables/internal/lookup.d.ts +2 -0
- package/dist/observables/internal/utils.d.ts +1 -1
- package/dist/observables/preact.d.ts +8 -2
- package/dist/r-state-tree.cjs +219 -17
- package/dist/r-state-tree.js +219 -17
- package/dist/store/StoreAdministration.d.ts +1 -1
- package/dist/types.d.ts +8 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,6 +42,19 @@ TypeScript 5+ supports TC39 Stage 3 decorators.
|
|
|
42
42
|
- `useDefineForClassFields: true` is recommended with modern toolchains targeting ES2022.
|
|
43
43
|
- The library includes a `Symbol.metadata` polyfill via `@tsmetadata/polyfill`.
|
|
44
44
|
|
|
45
|
+
### Vite / esbuild config (SSR)
|
|
46
|
+
|
|
47
|
+
When using Vite or esbuild for SSR, ensure the target is set to `es2022` to support Stage 3 decorators:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
// vite.config.ts
|
|
51
|
+
export default defineConfig({
|
|
52
|
+
esbuild: {
|
|
53
|
+
target: "es2022",
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
45
58
|
## Core concepts
|
|
46
59
|
|
|
47
60
|
- Stores: application/view state containers. Create with `createStore()`, attach with `mount()`. Compose with `@child` (single or arrays, stable via `{ key }`). React to changes with `effect`/`reaction` and store lifecycles (`storeDidMount`/`storeWillUnmount`). Update reactive `props` via `updateStore()`.
|
|
@@ -389,6 +402,55 @@ effect(() => {
|
|
|
389
402
|
state.count++; // Triggers the effect
|
|
390
403
|
```
|
|
391
404
|
|
|
405
|
+
### Shallow and signal observables
|
|
406
|
+
|
|
407
|
+
Control the depth of reactivity with `@observable.shallow` and `@observable.signal`:
|
|
408
|
+
|
|
409
|
+
| Decorator | Container Observable? | Values Observable? | Triggers on... |
|
|
410
|
+
|-----------|----------------------|-------------------|----------------|
|
|
411
|
+
| `@observable` | ✅ Yes | ✅ Yes (deep) | mutations + assignment |
|
|
412
|
+
| `@observable.shallow` | ✅ Yes | ❌ No | mutations + assignment |
|
|
413
|
+
| `@observable.signal` | ❌ No | ❌ No | assignment only |
|
|
414
|
+
|
|
415
|
+
```ts
|
|
416
|
+
import { Observable, observable, effect, isObservable } from "r-state-tree";
|
|
417
|
+
|
|
418
|
+
class DataStore extends Observable {
|
|
419
|
+
// Deep observable - items pushed are also observable
|
|
420
|
+
@observable deepItems: { value: number }[] = [];
|
|
421
|
+
|
|
422
|
+
// Shallow - container is observable, but pushed items are NOT
|
|
423
|
+
@observable.shallow shallowItems: { value: number }[] = [];
|
|
424
|
+
|
|
425
|
+
// Signal - only assignment triggers, mutations do not
|
|
426
|
+
@observable.signal signalItems: number[] = [];
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const store = new DataStore();
|
|
430
|
+
|
|
431
|
+
// Shallow: items pushed are NOT observable (safe for structuredClone)
|
|
432
|
+
store.shallowItems.push({ value: 1 });
|
|
433
|
+
console.log(isObservable(store.shallowItems[0])); // false
|
|
434
|
+
|
|
435
|
+
// Signal: mutations don't trigger effects
|
|
436
|
+
effect(() => store.signalItems.length);
|
|
437
|
+
store.signalItems.push(1); // Does NOT trigger effect
|
|
438
|
+
store.signalItems = [1, 2]; // DOES trigger effect
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
**Use cases:**
|
|
442
|
+
- `@observable.shallow`: Store external data that may need `structuredClone()`, or when you want change detection on the container but not deep reactivity
|
|
443
|
+
- `@observable.signal`: Maximum performance when you're replacing values rather than mutating them
|
|
444
|
+
|
|
445
|
+
Models support the same modifiers with `@state`:
|
|
446
|
+
|
|
447
|
+
```ts
|
|
448
|
+
class M extends Model {
|
|
449
|
+
@state.shallow items: { value: number }[] = []; // Container tracked, items not
|
|
450
|
+
@state.signal data: SomeType = null; // Only assignment tracked
|
|
451
|
+
}
|
|
452
|
+
```
|
|
453
|
+
|
|
392
454
|
## Observables (low‑level)
|
|
393
455
|
|
|
394
456
|
Create reactive structures outside Stores/Models. Supported: Objects, Arrays, Map, Set, WeakMap, WeakSet.
|
|
@@ -824,6 +886,31 @@ const off = onSnapshot(m, (snap) =>
|
|
|
824
886
|
- Forgetting stable keys for `@child` arrays causes identity churn.
|
|
825
887
|
- Assuming deep reactivity on undecorated fields of plain classes; use `@observable` for `Observable` classes, or use Stores/Models.
|
|
826
888
|
- Creating child stores in constructors: `@child` must be on getters so identity and lifecycle can be managed by the framework.
|
|
889
|
+
- Passing `models` into child stores during mount can create a recursive mount loop. If a child needs parent models, create the child store/model inside `storeDidMount` instead of wiring it through `models` during the mount cycle.
|
|
890
|
+
|
|
891
|
+
### Circular store/model creation
|
|
892
|
+
|
|
893
|
+
When child stores are created during mount with `models` that point back into the parent, it is easy to trigger an endless mount loop. The runtime now guards this by throwing a descriptive error (for example, `detected circular store/model creation while mounting ParentStore -> ChildStore.loop -> ...`). If you see this, move child creation into `storeDidMount` or break the cycle so that models are produced after the parent finishes mounting.
|
|
894
|
+
|
|
895
|
+
## LLM implementation checklist
|
|
896
|
+
|
|
897
|
+
- Do not thread context data via props. Provide contexts at the parent and consume them in children. Passing `getX` callbacks for resource/page/video/skill is a red flag—consume contexts instead.
|
|
898
|
+
- Avoid aggregating contexts into a single `ctx` object; consume where needed or expose small, focused getters.
|
|
899
|
+
- Only create stores in three places: root, `@child` getters, or immediately before mounting. Avoid standalone factory helpers; inline `createStore` in `@child` getters.
|
|
900
|
+
- Do not wrap `createStore` calls with `as Record<string, unknown>`; fix typing instead.
|
|
901
|
+
- Domain/persistent state belongs in Models; UI/app orchestration in Stores. Keep UI terms out of domain models—name domain concepts (e.g., `ChatThreadsModel`).
|
|
902
|
+
- If state is persistent/rehydratable, model it and derive stores from the model; keep purely view/ephemeral state in stores.
|
|
903
|
+
- Use r-state-tree snapshots (`toSnapshot`/`applySnapshot`) instead of hand-rolled `serialize`/`rehydrate` unless a different shape is required.
|
|
904
|
+
- Keep `onPersist` only when syncing store state into a backing model; otherwise prefer snapshot listeners.
|
|
905
|
+
- Pure, stateless helpers belong in utility modules, not as store methods. If a method does not touch `this`, extract it; keep coupled helpers in the store.
|
|
906
|
+
- Provide stable, rarely changing resource/view/video/page data via context (resourceId, resourceType, totalPages, current page/display mode, page offsets, document title, skill/detail, video info). Children should consume context directly.
|
|
907
|
+
- Use `@child` getters to create child stores with stable keys; avoid constructor creation. Pass only what the child needs; avoid prop drilling context.
|
|
908
|
+
- Eliminate blanket casts; fix types and let inference work. Avoid `any`.
|
|
909
|
+
- For `createStore` props, extend `Record<string, unknown>` only if needed; otherwise rely on proper prop types and context.
|
|
910
|
+
- Avoid barrel files if they cause import confusion; prefer direct imports.
|
|
911
|
+
- Keep file boundaries clean: one model per file; avoid piling multiple models together.
|
|
912
|
+
- Do not shadow `props` or use constructors for work better suited to `storeDidMount`.
|
|
913
|
+
- Use `@model` for injected models; `@child` for child stores; stable keys for arrays.
|
|
827
914
|
|
|
828
915
|
## Typing recipes
|
|
829
916
|
|
|
@@ -10,5 +10,7 @@ export declare function getObservableClassInstance<T extends object>(value: T):
|
|
|
10
10
|
export declare function getObservableIfExists<T>(value: T): T | undefined;
|
|
11
11
|
export declare function createObservableWithCustomAdministration<T>(value: T, Adm: new (obj: any) => Administration): T;
|
|
12
12
|
export declare function getObservable<T>(value: T): T;
|
|
13
|
+
export declare function isShallowObservable(obj: unknown): boolean;
|
|
14
|
+
export declare function getShallowObservable<T>(value: T): T;
|
|
13
15
|
export declare function isObservable(obj: unknown): boolean;
|
|
14
16
|
export declare function getInternalNode(obj: object, key?: PropertyKey): any;
|
|
@@ -2,7 +2,7 @@ import type { AtomNode, ComputedNode, SignalNode } from "../preact";
|
|
|
2
2
|
export declare function defaultEquals<T>(a: T, b: T): boolean;
|
|
3
3
|
export declare function isNonPrimitive(val: unknown): val is object;
|
|
4
4
|
export declare function isPropertyKey(val: unknown): val is string | number | symbol;
|
|
5
|
-
export type PropertyType = "action" | "computed" | "observable";
|
|
5
|
+
export type PropertyType = "action" | "computed" | "observable" | "observableShallow" | "observableSignal";
|
|
6
6
|
export declare function getPropertyType(key: PropertyKey, obj: object): PropertyType | null;
|
|
7
7
|
export declare function getPropertyDescriptor(obj: object, key: PropertyKey): PropertyDescriptor | undefined;
|
|
8
8
|
export declare function isPlainObject(value: unknown): value is object;
|
|
@@ -56,8 +56,14 @@ export type PreactObservable<T> = T extends Function ? T : T extends Map<infer K
|
|
|
56
56
|
} & {
|
|
57
57
|
readonly [key in keyof T as T[key] extends object ? never : `$${string & key}`]?: Signal<T[key]>;
|
|
58
58
|
};
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
declare function observableImpl<T>(obj: T): PreactObservable<T>;
|
|
60
|
+
declare function observableImpl(value: undefined, context: ClassFieldDecoratorContext): void;
|
|
61
|
+
declare function shallowDecorator(value: any, context: ClassFieldDecoratorContext): any;
|
|
62
|
+
declare function signalDecorator(value: any, context: ClassFieldDecoratorContext): any;
|
|
63
|
+
export declare const observable: typeof observableImpl & {
|
|
64
|
+
shallow: typeof shallowDecorator;
|
|
65
|
+
signal: typeof signalDecorator;
|
|
66
|
+
};
|
|
61
67
|
export declare function computed<T>(value: () => T, context: ClassGetterDecoratorContext): void;
|
|
62
68
|
export declare function computed<T>(fn: () => T): ReadonlySignal<T>;
|
|
63
69
|
export declare function source<T>(obj: PreactObservable<T> | T): T;
|
package/dist/r-state-tree.cjs
CHANGED
|
@@ -17,6 +17,8 @@ var CommonCfgTypes = /* @__PURE__ */ ((CommonCfgTypes2) => {
|
|
|
17
17
|
})(CommonCfgTypes || {});
|
|
18
18
|
var ModelCfgTypes = /* @__PURE__ */ ((ModelCfgTypes2) => {
|
|
19
19
|
ModelCfgTypes2["state"] = "state";
|
|
20
|
+
ModelCfgTypes2["stateShallow"] = "stateShallow";
|
|
21
|
+
ModelCfgTypes2["stateSignal"] = "stateSignal";
|
|
20
22
|
ModelCfgTypes2["id"] = "id";
|
|
21
23
|
ModelCfgTypes2["modelRef"] = "modelRef";
|
|
22
24
|
return ModelCfgTypes2;
|
|
@@ -27,6 +29,8 @@ var StoreCfgTypes = /* @__PURE__ */ ((StoreCfgTypes2) => {
|
|
|
27
29
|
})(StoreCfgTypes || {});
|
|
28
30
|
var ObservableCfgTypes = /* @__PURE__ */ ((ObservableCfgTypes2) => {
|
|
29
31
|
ObservableCfgTypes2["observable"] = "observable";
|
|
32
|
+
ObservableCfgTypes2["observableShallow"] = "observableShallow";
|
|
33
|
+
ObservableCfgTypes2["observableSignal"] = "observableSignal";
|
|
30
34
|
ObservableCfgTypes2["computed"] = "computed";
|
|
31
35
|
return ObservableCfgTypes2;
|
|
32
36
|
})(ObservableCfgTypes || {});
|
|
@@ -43,6 +47,14 @@ const stateType = {
|
|
|
43
47
|
type: "state"
|
|
44
48
|
/* state */
|
|
45
49
|
};
|
|
50
|
+
const stateShallowType = {
|
|
51
|
+
type: "stateShallow"
|
|
52
|
+
/* stateShallow */
|
|
53
|
+
};
|
|
54
|
+
const stateSignalType = {
|
|
55
|
+
type: "stateSignal"
|
|
56
|
+
/* stateSignal */
|
|
57
|
+
};
|
|
46
58
|
const modelRefType = Object.assign(
|
|
47
59
|
function(childType2) {
|
|
48
60
|
return { type: "modelRef", childType: childType2 };
|
|
@@ -85,6 +97,12 @@ function getPropertyType(key, obj) {
|
|
|
85
97
|
switch (config.type) {
|
|
86
98
|
case ObservableCfgTypes.computed:
|
|
87
99
|
return "computed";
|
|
100
|
+
case ObservableCfgTypes.observableShallow:
|
|
101
|
+
case ModelCfgTypes.stateShallow:
|
|
102
|
+
return "observableShallow";
|
|
103
|
+
case ObservableCfgTypes.observableSignal:
|
|
104
|
+
case ModelCfgTypes.stateSignal:
|
|
105
|
+
return "observableSignal";
|
|
88
106
|
case ModelCfgTypes.state:
|
|
89
107
|
case ObservableCfgTypes.observable:
|
|
90
108
|
case ModelCfgTypes.id:
|
|
@@ -353,6 +371,7 @@ class ObjectAdministration extends Administration {
|
|
|
353
371
|
}
|
|
354
372
|
switch (type) {
|
|
355
373
|
case "observable":
|
|
374
|
+
case "observableShallow":
|
|
356
375
|
case "action": {
|
|
357
376
|
if (key in this.source) {
|
|
358
377
|
this.valuesMap.reportObserved(key, this.source[key]);
|
|
@@ -364,8 +383,21 @@ class ObjectAdministration extends Administration {
|
|
|
364
383
|
if (type === "observable") {
|
|
365
384
|
return getObservable(this.get(key));
|
|
366
385
|
}
|
|
386
|
+
if (type === "observableShallow") {
|
|
387
|
+
return getShallowObservable(this.get(key));
|
|
388
|
+
}
|
|
367
389
|
return getAction(this.get(key));
|
|
368
390
|
}
|
|
391
|
+
case "observableSignal": {
|
|
392
|
+
if (key in this.source) {
|
|
393
|
+
this.valuesMap.reportObserved(key, this.source[key]);
|
|
394
|
+
}
|
|
395
|
+
this.atom.reportObserved();
|
|
396
|
+
if (this.atom.observing) {
|
|
397
|
+
this.hasMap.reportObserved(key);
|
|
398
|
+
}
|
|
399
|
+
return this.get(key);
|
|
400
|
+
}
|
|
369
401
|
case "computed": {
|
|
370
402
|
return this.callComputed(key);
|
|
371
403
|
}
|
|
@@ -573,13 +605,31 @@ function createComputed(fn, context = null) {
|
|
|
573
605
|
}
|
|
574
606
|
};
|
|
575
607
|
}
|
|
576
|
-
function
|
|
608
|
+
function observableImpl(value, context) {
|
|
577
609
|
if (context && typeof context === "object" && "kind" in context) {
|
|
578
610
|
context.metadata[context.name] = { type: "observable" };
|
|
579
611
|
return value;
|
|
580
612
|
}
|
|
581
613
|
return getObservable(value);
|
|
582
614
|
}
|
|
615
|
+
function shallowDecorator(value, context) {
|
|
616
|
+
if (context && typeof context === "object" && "kind" in context) {
|
|
617
|
+
context.metadata[context.name] = { type: "observableShallow" };
|
|
618
|
+
return value;
|
|
619
|
+
}
|
|
620
|
+
throw new Error("observable.shallow can only be used as a decorator");
|
|
621
|
+
}
|
|
622
|
+
function signalDecorator(value, context) {
|
|
623
|
+
if (context && typeof context === "object" && "kind" in context) {
|
|
624
|
+
context.metadata[context.name] = { type: "observableSignal" };
|
|
625
|
+
return value;
|
|
626
|
+
}
|
|
627
|
+
throw new Error("observable.signal can only be used as a decorator");
|
|
628
|
+
}
|
|
629
|
+
const observable = Object.assign(observableImpl, {
|
|
630
|
+
shallow: shallowDecorator,
|
|
631
|
+
signal: signalDecorator
|
|
632
|
+
});
|
|
583
633
|
function computed(value, context) {
|
|
584
634
|
if (context && typeof context === "object" && "kind" in context) {
|
|
585
635
|
context.metadata[context.name] = { type: "computed" };
|
|
@@ -791,6 +841,9 @@ class CollectionAdministration extends Administration {
|
|
|
791
841
|
const value = sourceMap.get(targetKey) ?? sourceMap.get(key);
|
|
792
842
|
if (has) {
|
|
793
843
|
this.valuesMap.reportObserved(key, value);
|
|
844
|
+
if (isShallowObservable(this.proxy)) {
|
|
845
|
+
return value;
|
|
846
|
+
}
|
|
794
847
|
return getObservable(value);
|
|
795
848
|
}
|
|
796
849
|
return void 0;
|
|
@@ -979,6 +1032,9 @@ class ArrayAdministration extends Administration {
|
|
|
979
1032
|
get(index) {
|
|
980
1033
|
this.atom.reportObserved();
|
|
981
1034
|
this.valuesMap.reportObserved(index, this.source[index]);
|
|
1035
|
+
if (isShallowObservable(this.proxy)) {
|
|
1036
|
+
return this.source[index];
|
|
1037
|
+
}
|
|
982
1038
|
return getObservable(this.source[index]);
|
|
983
1039
|
}
|
|
984
1040
|
set(index, newValue) {
|
|
@@ -1244,6 +1300,39 @@ function getObservable(value) {
|
|
|
1244
1300
|
}
|
|
1245
1301
|
return value;
|
|
1246
1302
|
}
|
|
1303
|
+
const shallowObservables = /* @__PURE__ */ new WeakSet();
|
|
1304
|
+
function isShallowObservable(obj) {
|
|
1305
|
+
if (!obj || typeof obj !== "object") return false;
|
|
1306
|
+
return shallowObservables.has(obj);
|
|
1307
|
+
}
|
|
1308
|
+
function getShallowObservable(value) {
|
|
1309
|
+
if (!value) {
|
|
1310
|
+
return value;
|
|
1311
|
+
}
|
|
1312
|
+
const existingAdm = getAdministration(value);
|
|
1313
|
+
if (existingAdm) {
|
|
1314
|
+
return existingAdm.proxy;
|
|
1315
|
+
}
|
|
1316
|
+
if ((typeof value === "object" || typeof value === "function") && !Object.isFrozen(value)) {
|
|
1317
|
+
const obj = value;
|
|
1318
|
+
let Adm = null;
|
|
1319
|
+
if (Array.isArray(obj)) {
|
|
1320
|
+
Adm = ArrayAdministration;
|
|
1321
|
+
} else if (obj instanceof Map || obj instanceof WeakMap) {
|
|
1322
|
+
Adm = CollectionAdministration;
|
|
1323
|
+
} else if (obj instanceof Set || obj instanceof WeakSet) {
|
|
1324
|
+
Adm = CollectionAdministration;
|
|
1325
|
+
}
|
|
1326
|
+
if (Adm) {
|
|
1327
|
+
const adm = new Adm(obj);
|
|
1328
|
+
administrationMap.set(adm.proxy, adm);
|
|
1329
|
+
administrationMap.set(adm.source, adm);
|
|
1330
|
+
shallowObservables.add(adm.proxy);
|
|
1331
|
+
return adm.proxy;
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
return value;
|
|
1335
|
+
}
|
|
1247
1336
|
function isObservable(obj) {
|
|
1248
1337
|
if (!obj || typeof obj !== "object") return false;
|
|
1249
1338
|
const adm = getAdministration(obj);
|
|
@@ -1317,6 +1406,33 @@ function getDiff(o1, o2, getConfig) {
|
|
|
1317
1406
|
}
|
|
1318
1407
|
return Object.keys(diff).length > 0 ? diff : null;
|
|
1319
1408
|
}
|
|
1409
|
+
const MAX_MOUNT_DEPTH = 100;
|
|
1410
|
+
const mountingStack = [];
|
|
1411
|
+
function formatMountFrame(frame) {
|
|
1412
|
+
const childSegment = frame.childName === void 0 ? "" : `.${String(frame.childName)}`;
|
|
1413
|
+
return `${frame.storeName}${childSegment}`;
|
|
1414
|
+
}
|
|
1415
|
+
function formatMountChain(frame) {
|
|
1416
|
+
const chain = [...mountingStack, frame];
|
|
1417
|
+
const maxParts = 6;
|
|
1418
|
+
if (chain.length > maxParts) {
|
|
1419
|
+
const start = chain.slice(0, 3);
|
|
1420
|
+
const end = chain.slice(-2);
|
|
1421
|
+
return [
|
|
1422
|
+
...start,
|
|
1423
|
+
{ storeName: "...", modelsKeys: [], childName: void 0 },
|
|
1424
|
+
...end
|
|
1425
|
+
].map(formatMountFrame).join(" -> ");
|
|
1426
|
+
}
|
|
1427
|
+
return chain.map(formatMountFrame).join(" -> ");
|
|
1428
|
+
}
|
|
1429
|
+
function createCircularMountError(frame) {
|
|
1430
|
+
const chain = formatMountChain(frame);
|
|
1431
|
+
const models = frame.modelsKeys.length === 0 ? "no models provided" : `models: ${frame.modelsKeys.join(", ")}`;
|
|
1432
|
+
return new Error(
|
|
1433
|
+
`r-state-tree: detected circular store/model creation while mounting ${chain} (using ${models}). Passing models into child stores during mount can create recursive wiring. Move child store/model creation into storeDidMount or break the cycle.`
|
|
1434
|
+
);
|
|
1435
|
+
}
|
|
1320
1436
|
function updateProps(props, newProps) {
|
|
1321
1437
|
signalsCore.untracked(() => {
|
|
1322
1438
|
signalsCore.batch(() => {
|
|
@@ -1336,6 +1452,35 @@ function updateProps(props, newProps) {
|
|
|
1336
1452
|
function getStoreAdm(store) {
|
|
1337
1453
|
return getAdministration(store);
|
|
1338
1454
|
}
|
|
1455
|
+
function validateStoreChildValue(value, propertyName) {
|
|
1456
|
+
if (value === null || value === void 0) {
|
|
1457
|
+
return;
|
|
1458
|
+
}
|
|
1459
|
+
if (Array.isArray(value)) {
|
|
1460
|
+
const invalidItem = value.find(
|
|
1461
|
+
(item) => item !== null && (typeof item !== "object" || !("Type" in item) || !("props" in item) || typeof item.Type !== "function" || typeof item.props !== "object")
|
|
1462
|
+
);
|
|
1463
|
+
if (invalidItem !== void 0) {
|
|
1464
|
+
throw new Error(
|
|
1465
|
+
`r-state-tree: child property '${String(
|
|
1466
|
+
propertyName
|
|
1467
|
+
)}' must be a StoreElement ({ Type, props, key }), an array of StoreElements, or null/undefined. Found invalid array item: ${typeof invalidItem}`
|
|
1468
|
+
);
|
|
1469
|
+
}
|
|
1470
|
+
return;
|
|
1471
|
+
}
|
|
1472
|
+
if (typeof value === "object" && value !== null && "Type" in value && "props" in value) {
|
|
1473
|
+
const element = value;
|
|
1474
|
+
if (typeof element.Type === "function" && typeof element.props === "object") {
|
|
1475
|
+
return;
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
throw new Error(
|
|
1479
|
+
`r-state-tree: child property '${String(
|
|
1480
|
+
propertyName
|
|
1481
|
+
)}' must be a StoreElement ({ Type, props, key }), an array of StoreElements, or null/undefined. Found: ${typeof value}`
|
|
1482
|
+
);
|
|
1483
|
+
}
|
|
1339
1484
|
class StoreAdministration extends PreactObjectAdministration {
|
|
1340
1485
|
static proxyTraps = Object.assign(
|
|
1341
1486
|
{},
|
|
@@ -1403,7 +1548,7 @@ class StoreAdministration extends PreactObjectAdministration {
|
|
|
1403
1548
|
}
|
|
1404
1549
|
});
|
|
1405
1550
|
childStoreData.value.set(stores);
|
|
1406
|
-
stores.forEach((s) => getStoreAdm(s).mount(this));
|
|
1551
|
+
stores.forEach((s) => getStoreAdm(s).mount(this, name));
|
|
1407
1552
|
return stores;
|
|
1408
1553
|
}
|
|
1409
1554
|
const newStores = /* @__PURE__ */ new Set();
|
|
@@ -1454,7 +1599,7 @@ class StoreAdministration extends PreactObjectAdministration {
|
|
|
1454
1599
|
signalsCore.batch(() => childStoreData.value.set(stores));
|
|
1455
1600
|
}
|
|
1456
1601
|
removedStores.forEach((s) => getStoreAdm(s).unmount());
|
|
1457
|
-
newStores.forEach((s) => getStoreAdm(s).mount(this));
|
|
1602
|
+
newStores.forEach((s) => getStoreAdm(s).mount(this, name));
|
|
1458
1603
|
return stores;
|
|
1459
1604
|
}
|
|
1460
1605
|
setSingleStore(name, element) {
|
|
@@ -1473,7 +1618,7 @@ class StoreAdministration extends PreactObjectAdministration {
|
|
|
1473
1618
|
}
|
|
1474
1619
|
const childStore = this.createChildStore(element);
|
|
1475
1620
|
signalsCore.batch(() => childStoreData.value.set(childStore));
|
|
1476
|
-
getStoreAdm(childStore).mount(this);
|
|
1621
|
+
getStoreAdm(childStore).mount(this, name);
|
|
1477
1622
|
return childStore;
|
|
1478
1623
|
} else {
|
|
1479
1624
|
signalsCore.batch(() => updateProps(oldStore.props, props));
|
|
@@ -1485,6 +1630,7 @@ class StoreAdministration extends PreactObjectAdministration {
|
|
|
1485
1630
|
const storeElement = childStoreData.listener.track(
|
|
1486
1631
|
() => childStoreData.computed.get()
|
|
1487
1632
|
);
|
|
1633
|
+
validateStoreChildValue(storeElement, name);
|
|
1488
1634
|
Array.isArray(storeElement) ? this.setStoreList(name, storeElement) : this.setSingleStore(name, storeElement);
|
|
1489
1635
|
}
|
|
1490
1636
|
getComputedGetter(name) {
|
|
@@ -1505,6 +1651,7 @@ class StoreAdministration extends PreactObjectAdministration {
|
|
|
1505
1651
|
const storeElement = childStoreData.listener.track(
|
|
1506
1652
|
() => childStoreData.computed.get()
|
|
1507
1653
|
);
|
|
1654
|
+
validateStoreChildValue(storeElement, name);
|
|
1508
1655
|
Array.isArray(storeElement) ? this.setStoreList(name, storeElement) : this.setSingleStore(name, storeElement);
|
|
1509
1656
|
return childStoreData.value.get();
|
|
1510
1657
|
}
|
|
@@ -1514,6 +1661,7 @@ class StoreAdministration extends PreactObjectAdministration {
|
|
|
1514
1661
|
return this.initializeStore(name);
|
|
1515
1662
|
} else {
|
|
1516
1663
|
const storeElement = signalsCore.untracked(() => childStoreData.computed.get());
|
|
1664
|
+
validateStoreChildValue(storeElement, name);
|
|
1517
1665
|
Array.isArray(storeElement) ? this.setStoreList(name, storeElement) : this.setSingleStore(name, storeElement);
|
|
1518
1666
|
return childStoreData.value.get();
|
|
1519
1667
|
}
|
|
@@ -1562,18 +1710,36 @@ class StoreAdministration extends PreactObjectAdministration {
|
|
|
1562
1710
|
this.reactionsUnsub.push(unsub);
|
|
1563
1711
|
return unsub;
|
|
1564
1712
|
}
|
|
1565
|
-
mount(parent = null) {
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1713
|
+
mount(parent = null, childName) {
|
|
1714
|
+
const frame = {
|
|
1715
|
+
storeName: this.proxy.constructor.name || "Store",
|
|
1716
|
+
childName,
|
|
1717
|
+
modelsKeys: Object.keys(this.proxy.props?.models ?? {})
|
|
1718
|
+
};
|
|
1719
|
+
if (mountingStack.length + 1 > MAX_MOUNT_DEPTH) {
|
|
1720
|
+
throw createCircularMountError(frame);
|
|
1721
|
+
}
|
|
1722
|
+
mountingStack.push(frame);
|
|
1723
|
+
try {
|
|
1724
|
+
this.parent = parent || null;
|
|
1725
|
+
this.childStoreDataMap.forEach(({ value }, name) => {
|
|
1726
|
+
const stores = value.get();
|
|
1727
|
+
if (Array.isArray(stores)) {
|
|
1728
|
+
stores?.forEach((s) => getStoreAdm(s)?.mount(this, name));
|
|
1729
|
+
} else if (stores) {
|
|
1730
|
+
getStoreAdm(stores)?.mount(this, name);
|
|
1731
|
+
}
|
|
1732
|
+
});
|
|
1733
|
+
this.mounted = true;
|
|
1734
|
+
signalsCore.batch(() => this.proxy.storeDidMount?.());
|
|
1735
|
+
} catch (error) {
|
|
1736
|
+
if (error instanceof RangeError && /call stack/i.test(error.message)) {
|
|
1737
|
+
throw createCircularMountError(frame);
|
|
1573
1738
|
}
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1739
|
+
throw error;
|
|
1740
|
+
} finally {
|
|
1741
|
+
mountingStack.pop();
|
|
1742
|
+
}
|
|
1577
1743
|
}
|
|
1578
1744
|
unmount() {
|
|
1579
1745
|
this.proxy.storeWillUnmount?.();
|
|
@@ -1891,6 +2057,30 @@ function getSnapshotRefId(snapshot) {
|
|
|
1891
2057
|
}
|
|
1892
2058
|
return snapshot[keys[0]];
|
|
1893
2059
|
}
|
|
2060
|
+
function validateModelChildValue(value, propertyName) {
|
|
2061
|
+
if (value === null || value === void 0) {
|
|
2062
|
+
return;
|
|
2063
|
+
}
|
|
2064
|
+
if (value instanceof Model) {
|
|
2065
|
+
return;
|
|
2066
|
+
}
|
|
2067
|
+
if (Array.isArray(value)) {
|
|
2068
|
+
const invalidItem = value.find((item) => !(item instanceof Model));
|
|
2069
|
+
if (invalidItem !== void 0) {
|
|
2070
|
+
throw new Error(
|
|
2071
|
+
`r-state-tree: child property '${String(
|
|
2072
|
+
propertyName
|
|
2073
|
+
)}' must be a Model instance, an array of Model instances, or null/undefined. Found invalid array item: ${typeof invalidItem}`
|
|
2074
|
+
);
|
|
2075
|
+
}
|
|
2076
|
+
return;
|
|
2077
|
+
}
|
|
2078
|
+
throw new Error(
|
|
2079
|
+
`r-state-tree: child property '${String(
|
|
2080
|
+
propertyName
|
|
2081
|
+
)}' must be a Model instance, an array of Model instances, or null/undefined. Found: ${typeof value}`
|
|
2082
|
+
);
|
|
2083
|
+
}
|
|
1894
2084
|
class ModelAdministration extends PreactObjectAdministration {
|
|
1895
2085
|
static proxyTraps = Object.assign(
|
|
1896
2086
|
{},
|
|
@@ -1924,6 +2114,7 @@ class ModelAdministration extends PreactObjectAdministration {
|
|
|
1924
2114
|
return true;
|
|
1925
2115
|
}
|
|
1926
2116
|
case CommonCfgTypes.child: {
|
|
2117
|
+
validateModelChildValue(value, name);
|
|
1927
2118
|
if (Array.isArray(value)) {
|
|
1928
2119
|
adm.setModels(name, value);
|
|
1929
2120
|
return true;
|
|
@@ -1936,7 +2127,9 @@ class ModelAdministration extends PreactObjectAdministration {
|
|
|
1936
2127
|
adm.setId(name, value);
|
|
1937
2128
|
break;
|
|
1938
2129
|
}
|
|
1939
|
-
case ModelCfgTypes.state:
|
|
2130
|
+
case ModelCfgTypes.state:
|
|
2131
|
+
case ModelCfgTypes.stateShallow:
|
|
2132
|
+
case ModelCfgTypes.stateSignal: {
|
|
1940
2133
|
adm.setState(name, value);
|
|
1941
2134
|
break;
|
|
1942
2135
|
}
|
|
@@ -2030,6 +2223,7 @@ class ModelAdministration extends PreactObjectAdministration {
|
|
|
2030
2223
|
}
|
|
2031
2224
|
}
|
|
2032
2225
|
setModel(name, newModel) {
|
|
2226
|
+
validateModelChildValue(newModel, name);
|
|
2033
2227
|
const currentValue = this.proxy[name];
|
|
2034
2228
|
if (currentValue === newModel) {
|
|
2035
2229
|
return;
|
|
@@ -2049,6 +2243,7 @@ class ModelAdministration extends PreactObjectAdministration {
|
|
|
2049
2243
|
}
|
|
2050
2244
|
}
|
|
2051
2245
|
setModels(name, newModelsSource) {
|
|
2246
|
+
validateModelChildValue(newModelsSource, name);
|
|
2052
2247
|
const newModels = createObservableWithCustomAdministration(
|
|
2053
2248
|
[],
|
|
2054
2249
|
ChildModelsAdministration
|
|
@@ -2204,6 +2399,8 @@ class ModelAdministration extends PreactObjectAdministration {
|
|
|
2204
2399
|
return Object.keys(this.configuration).reduce((json, key) => {
|
|
2205
2400
|
switch (this.configuration[key].type) {
|
|
2206
2401
|
case ModelCfgTypes.state:
|
|
2402
|
+
case ModelCfgTypes.stateShallow:
|
|
2403
|
+
case ModelCfgTypes.stateSignal:
|
|
2207
2404
|
case ModelCfgTypes.id:
|
|
2208
2405
|
json[key] = clone(getSource(this.proxy[key]));
|
|
2209
2406
|
break;
|
|
@@ -2269,6 +2466,8 @@ class ModelAdministration extends PreactObjectAdministration {
|
|
|
2269
2466
|
const value = snapshot[key];
|
|
2270
2467
|
switch (type) {
|
|
2271
2468
|
case ModelCfgTypes.state:
|
|
2469
|
+
case ModelCfgTypes.stateShallow:
|
|
2470
|
+
case ModelCfgTypes.stateSignal:
|
|
2272
2471
|
this.proxy[key] = value;
|
|
2273
2472
|
break;
|
|
2274
2473
|
case ModelCfgTypes.modelRef:
|
|
@@ -2484,7 +2683,10 @@ const child = makeChildDecorator(childType);
|
|
|
2484
2683
|
const modelRef = makeChildDecorator(modelRefType);
|
|
2485
2684
|
const model = makeDecorator(modelType);
|
|
2486
2685
|
const id = makeDecorator(idType);
|
|
2487
|
-
const state = makeDecorator(stateType)
|
|
2686
|
+
const state = Object.assign(makeDecorator(stateType), {
|
|
2687
|
+
shallow: makeDecorator(stateShallowType),
|
|
2688
|
+
signal: makeDecorator(stateSignalType)
|
|
2689
|
+
});
|
|
2488
2690
|
Object.defineProperty(exports, "Signal", {
|
|
2489
2691
|
enumerable: true,
|
|
2490
2692
|
get: () => signalsCore.Signal
|
package/dist/r-state-tree.js
CHANGED
|
@@ -16,6 +16,8 @@ var CommonCfgTypes = /* @__PURE__ */ ((CommonCfgTypes2) => {
|
|
|
16
16
|
})(CommonCfgTypes || {});
|
|
17
17
|
var ModelCfgTypes = /* @__PURE__ */ ((ModelCfgTypes2) => {
|
|
18
18
|
ModelCfgTypes2["state"] = "state";
|
|
19
|
+
ModelCfgTypes2["stateShallow"] = "stateShallow";
|
|
20
|
+
ModelCfgTypes2["stateSignal"] = "stateSignal";
|
|
19
21
|
ModelCfgTypes2["id"] = "id";
|
|
20
22
|
ModelCfgTypes2["modelRef"] = "modelRef";
|
|
21
23
|
return ModelCfgTypes2;
|
|
@@ -26,6 +28,8 @@ var StoreCfgTypes = /* @__PURE__ */ ((StoreCfgTypes2) => {
|
|
|
26
28
|
})(StoreCfgTypes || {});
|
|
27
29
|
var ObservableCfgTypes = /* @__PURE__ */ ((ObservableCfgTypes2) => {
|
|
28
30
|
ObservableCfgTypes2["observable"] = "observable";
|
|
31
|
+
ObservableCfgTypes2["observableShallow"] = "observableShallow";
|
|
32
|
+
ObservableCfgTypes2["observableSignal"] = "observableSignal";
|
|
29
33
|
ObservableCfgTypes2["computed"] = "computed";
|
|
30
34
|
return ObservableCfgTypes2;
|
|
31
35
|
})(ObservableCfgTypes || {});
|
|
@@ -42,6 +46,14 @@ const stateType = {
|
|
|
42
46
|
type: "state"
|
|
43
47
|
/* state */
|
|
44
48
|
};
|
|
49
|
+
const stateShallowType = {
|
|
50
|
+
type: "stateShallow"
|
|
51
|
+
/* stateShallow */
|
|
52
|
+
};
|
|
53
|
+
const stateSignalType = {
|
|
54
|
+
type: "stateSignal"
|
|
55
|
+
/* stateSignal */
|
|
56
|
+
};
|
|
45
57
|
const modelRefType = Object.assign(
|
|
46
58
|
function(childType2) {
|
|
47
59
|
return { type: "modelRef", childType: childType2 };
|
|
@@ -84,6 +96,12 @@ function getPropertyType(key, obj) {
|
|
|
84
96
|
switch (config.type) {
|
|
85
97
|
case ObservableCfgTypes.computed:
|
|
86
98
|
return "computed";
|
|
99
|
+
case ObservableCfgTypes.observableShallow:
|
|
100
|
+
case ModelCfgTypes.stateShallow:
|
|
101
|
+
return "observableShallow";
|
|
102
|
+
case ObservableCfgTypes.observableSignal:
|
|
103
|
+
case ModelCfgTypes.stateSignal:
|
|
104
|
+
return "observableSignal";
|
|
87
105
|
case ModelCfgTypes.state:
|
|
88
106
|
case ObservableCfgTypes.observable:
|
|
89
107
|
case ModelCfgTypes.id:
|
|
@@ -352,6 +370,7 @@ class ObjectAdministration extends Administration {
|
|
|
352
370
|
}
|
|
353
371
|
switch (type) {
|
|
354
372
|
case "observable":
|
|
373
|
+
case "observableShallow":
|
|
355
374
|
case "action": {
|
|
356
375
|
if (key in this.source) {
|
|
357
376
|
this.valuesMap.reportObserved(key, this.source[key]);
|
|
@@ -363,8 +382,21 @@ class ObjectAdministration extends Administration {
|
|
|
363
382
|
if (type === "observable") {
|
|
364
383
|
return getObservable(this.get(key));
|
|
365
384
|
}
|
|
385
|
+
if (type === "observableShallow") {
|
|
386
|
+
return getShallowObservable(this.get(key));
|
|
387
|
+
}
|
|
366
388
|
return getAction(this.get(key));
|
|
367
389
|
}
|
|
390
|
+
case "observableSignal": {
|
|
391
|
+
if (key in this.source) {
|
|
392
|
+
this.valuesMap.reportObserved(key, this.source[key]);
|
|
393
|
+
}
|
|
394
|
+
this.atom.reportObserved();
|
|
395
|
+
if (this.atom.observing) {
|
|
396
|
+
this.hasMap.reportObserved(key);
|
|
397
|
+
}
|
|
398
|
+
return this.get(key);
|
|
399
|
+
}
|
|
368
400
|
case "computed": {
|
|
369
401
|
return this.callComputed(key);
|
|
370
402
|
}
|
|
@@ -572,13 +604,31 @@ function createComputed(fn, context = null) {
|
|
|
572
604
|
}
|
|
573
605
|
};
|
|
574
606
|
}
|
|
575
|
-
function
|
|
607
|
+
function observableImpl(value, context) {
|
|
576
608
|
if (context && typeof context === "object" && "kind" in context) {
|
|
577
609
|
context.metadata[context.name] = { type: "observable" };
|
|
578
610
|
return value;
|
|
579
611
|
}
|
|
580
612
|
return getObservable(value);
|
|
581
613
|
}
|
|
614
|
+
function shallowDecorator(value, context) {
|
|
615
|
+
if (context && typeof context === "object" && "kind" in context) {
|
|
616
|
+
context.metadata[context.name] = { type: "observableShallow" };
|
|
617
|
+
return value;
|
|
618
|
+
}
|
|
619
|
+
throw new Error("observable.shallow can only be used as a decorator");
|
|
620
|
+
}
|
|
621
|
+
function signalDecorator(value, context) {
|
|
622
|
+
if (context && typeof context === "object" && "kind" in context) {
|
|
623
|
+
context.metadata[context.name] = { type: "observableSignal" };
|
|
624
|
+
return value;
|
|
625
|
+
}
|
|
626
|
+
throw new Error("observable.signal can only be used as a decorator");
|
|
627
|
+
}
|
|
628
|
+
const observable = Object.assign(observableImpl, {
|
|
629
|
+
shallow: shallowDecorator,
|
|
630
|
+
signal: signalDecorator
|
|
631
|
+
});
|
|
582
632
|
function computed(value, context) {
|
|
583
633
|
if (context && typeof context === "object" && "kind" in context) {
|
|
584
634
|
context.metadata[context.name] = { type: "computed" };
|
|
@@ -790,6 +840,9 @@ class CollectionAdministration extends Administration {
|
|
|
790
840
|
const value = sourceMap.get(targetKey) ?? sourceMap.get(key);
|
|
791
841
|
if (has) {
|
|
792
842
|
this.valuesMap.reportObserved(key, value);
|
|
843
|
+
if (isShallowObservable(this.proxy)) {
|
|
844
|
+
return value;
|
|
845
|
+
}
|
|
793
846
|
return getObservable(value);
|
|
794
847
|
}
|
|
795
848
|
return void 0;
|
|
@@ -978,6 +1031,9 @@ class ArrayAdministration extends Administration {
|
|
|
978
1031
|
get(index) {
|
|
979
1032
|
this.atom.reportObserved();
|
|
980
1033
|
this.valuesMap.reportObserved(index, this.source[index]);
|
|
1034
|
+
if (isShallowObservable(this.proxy)) {
|
|
1035
|
+
return this.source[index];
|
|
1036
|
+
}
|
|
981
1037
|
return getObservable(this.source[index]);
|
|
982
1038
|
}
|
|
983
1039
|
set(index, newValue) {
|
|
@@ -1243,6 +1299,39 @@ function getObservable(value) {
|
|
|
1243
1299
|
}
|
|
1244
1300
|
return value;
|
|
1245
1301
|
}
|
|
1302
|
+
const shallowObservables = /* @__PURE__ */ new WeakSet();
|
|
1303
|
+
function isShallowObservable(obj) {
|
|
1304
|
+
if (!obj || typeof obj !== "object") return false;
|
|
1305
|
+
return shallowObservables.has(obj);
|
|
1306
|
+
}
|
|
1307
|
+
function getShallowObservable(value) {
|
|
1308
|
+
if (!value) {
|
|
1309
|
+
return value;
|
|
1310
|
+
}
|
|
1311
|
+
const existingAdm = getAdministration(value);
|
|
1312
|
+
if (existingAdm) {
|
|
1313
|
+
return existingAdm.proxy;
|
|
1314
|
+
}
|
|
1315
|
+
if ((typeof value === "object" || typeof value === "function") && !Object.isFrozen(value)) {
|
|
1316
|
+
const obj = value;
|
|
1317
|
+
let Adm = null;
|
|
1318
|
+
if (Array.isArray(obj)) {
|
|
1319
|
+
Adm = ArrayAdministration;
|
|
1320
|
+
} else if (obj instanceof Map || obj instanceof WeakMap) {
|
|
1321
|
+
Adm = CollectionAdministration;
|
|
1322
|
+
} else if (obj instanceof Set || obj instanceof WeakSet) {
|
|
1323
|
+
Adm = CollectionAdministration;
|
|
1324
|
+
}
|
|
1325
|
+
if (Adm) {
|
|
1326
|
+
const adm = new Adm(obj);
|
|
1327
|
+
administrationMap.set(adm.proxy, adm);
|
|
1328
|
+
administrationMap.set(adm.source, adm);
|
|
1329
|
+
shallowObservables.add(adm.proxy);
|
|
1330
|
+
return adm.proxy;
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
return value;
|
|
1334
|
+
}
|
|
1246
1335
|
function isObservable(obj) {
|
|
1247
1336
|
if (!obj || typeof obj !== "object") return false;
|
|
1248
1337
|
const adm = getAdministration(obj);
|
|
@@ -1316,6 +1405,33 @@ function getDiff(o1, o2, getConfig) {
|
|
|
1316
1405
|
}
|
|
1317
1406
|
return Object.keys(diff).length > 0 ? diff : null;
|
|
1318
1407
|
}
|
|
1408
|
+
const MAX_MOUNT_DEPTH = 100;
|
|
1409
|
+
const mountingStack = [];
|
|
1410
|
+
function formatMountFrame(frame) {
|
|
1411
|
+
const childSegment = frame.childName === void 0 ? "" : `.${String(frame.childName)}`;
|
|
1412
|
+
return `${frame.storeName}${childSegment}`;
|
|
1413
|
+
}
|
|
1414
|
+
function formatMountChain(frame) {
|
|
1415
|
+
const chain = [...mountingStack, frame];
|
|
1416
|
+
const maxParts = 6;
|
|
1417
|
+
if (chain.length > maxParts) {
|
|
1418
|
+
const start = chain.slice(0, 3);
|
|
1419
|
+
const end = chain.slice(-2);
|
|
1420
|
+
return [
|
|
1421
|
+
...start,
|
|
1422
|
+
{ storeName: "...", modelsKeys: [], childName: void 0 },
|
|
1423
|
+
...end
|
|
1424
|
+
].map(formatMountFrame).join(" -> ");
|
|
1425
|
+
}
|
|
1426
|
+
return chain.map(formatMountFrame).join(" -> ");
|
|
1427
|
+
}
|
|
1428
|
+
function createCircularMountError(frame) {
|
|
1429
|
+
const chain = formatMountChain(frame);
|
|
1430
|
+
const models = frame.modelsKeys.length === 0 ? "no models provided" : `models: ${frame.modelsKeys.join(", ")}`;
|
|
1431
|
+
return new Error(
|
|
1432
|
+
`r-state-tree: detected circular store/model creation while mounting ${chain} (using ${models}). Passing models into child stores during mount can create recursive wiring. Move child store/model creation into storeDidMount or break the cycle.`
|
|
1433
|
+
);
|
|
1434
|
+
}
|
|
1319
1435
|
function updateProps(props, newProps) {
|
|
1320
1436
|
untracked(() => {
|
|
1321
1437
|
batch(() => {
|
|
@@ -1335,6 +1451,35 @@ function updateProps(props, newProps) {
|
|
|
1335
1451
|
function getStoreAdm(store) {
|
|
1336
1452
|
return getAdministration(store);
|
|
1337
1453
|
}
|
|
1454
|
+
function validateStoreChildValue(value, propertyName) {
|
|
1455
|
+
if (value === null || value === void 0) {
|
|
1456
|
+
return;
|
|
1457
|
+
}
|
|
1458
|
+
if (Array.isArray(value)) {
|
|
1459
|
+
const invalidItem = value.find(
|
|
1460
|
+
(item) => item !== null && (typeof item !== "object" || !("Type" in item) || !("props" in item) || typeof item.Type !== "function" || typeof item.props !== "object")
|
|
1461
|
+
);
|
|
1462
|
+
if (invalidItem !== void 0) {
|
|
1463
|
+
throw new Error(
|
|
1464
|
+
`r-state-tree: child property '${String(
|
|
1465
|
+
propertyName
|
|
1466
|
+
)}' must be a StoreElement ({ Type, props, key }), an array of StoreElements, or null/undefined. Found invalid array item: ${typeof invalidItem}`
|
|
1467
|
+
);
|
|
1468
|
+
}
|
|
1469
|
+
return;
|
|
1470
|
+
}
|
|
1471
|
+
if (typeof value === "object" && value !== null && "Type" in value && "props" in value) {
|
|
1472
|
+
const element = value;
|
|
1473
|
+
if (typeof element.Type === "function" && typeof element.props === "object") {
|
|
1474
|
+
return;
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
throw new Error(
|
|
1478
|
+
`r-state-tree: child property '${String(
|
|
1479
|
+
propertyName
|
|
1480
|
+
)}' must be a StoreElement ({ Type, props, key }), an array of StoreElements, or null/undefined. Found: ${typeof value}`
|
|
1481
|
+
);
|
|
1482
|
+
}
|
|
1338
1483
|
class StoreAdministration extends PreactObjectAdministration {
|
|
1339
1484
|
static proxyTraps = Object.assign(
|
|
1340
1485
|
{},
|
|
@@ -1402,7 +1547,7 @@ class StoreAdministration extends PreactObjectAdministration {
|
|
|
1402
1547
|
}
|
|
1403
1548
|
});
|
|
1404
1549
|
childStoreData.value.set(stores);
|
|
1405
|
-
stores.forEach((s) => getStoreAdm(s).mount(this));
|
|
1550
|
+
stores.forEach((s) => getStoreAdm(s).mount(this, name));
|
|
1406
1551
|
return stores;
|
|
1407
1552
|
}
|
|
1408
1553
|
const newStores = /* @__PURE__ */ new Set();
|
|
@@ -1453,7 +1598,7 @@ class StoreAdministration extends PreactObjectAdministration {
|
|
|
1453
1598
|
batch(() => childStoreData.value.set(stores));
|
|
1454
1599
|
}
|
|
1455
1600
|
removedStores.forEach((s) => getStoreAdm(s).unmount());
|
|
1456
|
-
newStores.forEach((s) => getStoreAdm(s).mount(this));
|
|
1601
|
+
newStores.forEach((s) => getStoreAdm(s).mount(this, name));
|
|
1457
1602
|
return stores;
|
|
1458
1603
|
}
|
|
1459
1604
|
setSingleStore(name, element) {
|
|
@@ -1472,7 +1617,7 @@ class StoreAdministration extends PreactObjectAdministration {
|
|
|
1472
1617
|
}
|
|
1473
1618
|
const childStore = this.createChildStore(element);
|
|
1474
1619
|
batch(() => childStoreData.value.set(childStore));
|
|
1475
|
-
getStoreAdm(childStore).mount(this);
|
|
1620
|
+
getStoreAdm(childStore).mount(this, name);
|
|
1476
1621
|
return childStore;
|
|
1477
1622
|
} else {
|
|
1478
1623
|
batch(() => updateProps(oldStore.props, props));
|
|
@@ -1484,6 +1629,7 @@ class StoreAdministration extends PreactObjectAdministration {
|
|
|
1484
1629
|
const storeElement = childStoreData.listener.track(
|
|
1485
1630
|
() => childStoreData.computed.get()
|
|
1486
1631
|
);
|
|
1632
|
+
validateStoreChildValue(storeElement, name);
|
|
1487
1633
|
Array.isArray(storeElement) ? this.setStoreList(name, storeElement) : this.setSingleStore(name, storeElement);
|
|
1488
1634
|
}
|
|
1489
1635
|
getComputedGetter(name) {
|
|
@@ -1504,6 +1650,7 @@ class StoreAdministration extends PreactObjectAdministration {
|
|
|
1504
1650
|
const storeElement = childStoreData.listener.track(
|
|
1505
1651
|
() => childStoreData.computed.get()
|
|
1506
1652
|
);
|
|
1653
|
+
validateStoreChildValue(storeElement, name);
|
|
1507
1654
|
Array.isArray(storeElement) ? this.setStoreList(name, storeElement) : this.setSingleStore(name, storeElement);
|
|
1508
1655
|
return childStoreData.value.get();
|
|
1509
1656
|
}
|
|
@@ -1513,6 +1660,7 @@ class StoreAdministration extends PreactObjectAdministration {
|
|
|
1513
1660
|
return this.initializeStore(name);
|
|
1514
1661
|
} else {
|
|
1515
1662
|
const storeElement = untracked(() => childStoreData.computed.get());
|
|
1663
|
+
validateStoreChildValue(storeElement, name);
|
|
1516
1664
|
Array.isArray(storeElement) ? this.setStoreList(name, storeElement) : this.setSingleStore(name, storeElement);
|
|
1517
1665
|
return childStoreData.value.get();
|
|
1518
1666
|
}
|
|
@@ -1561,18 +1709,36 @@ class StoreAdministration extends PreactObjectAdministration {
|
|
|
1561
1709
|
this.reactionsUnsub.push(unsub);
|
|
1562
1710
|
return unsub;
|
|
1563
1711
|
}
|
|
1564
|
-
mount(parent = null) {
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1712
|
+
mount(parent = null, childName) {
|
|
1713
|
+
const frame = {
|
|
1714
|
+
storeName: this.proxy.constructor.name || "Store",
|
|
1715
|
+
childName,
|
|
1716
|
+
modelsKeys: Object.keys(this.proxy.props?.models ?? {})
|
|
1717
|
+
};
|
|
1718
|
+
if (mountingStack.length + 1 > MAX_MOUNT_DEPTH) {
|
|
1719
|
+
throw createCircularMountError(frame);
|
|
1720
|
+
}
|
|
1721
|
+
mountingStack.push(frame);
|
|
1722
|
+
try {
|
|
1723
|
+
this.parent = parent || null;
|
|
1724
|
+
this.childStoreDataMap.forEach(({ value }, name) => {
|
|
1725
|
+
const stores = value.get();
|
|
1726
|
+
if (Array.isArray(stores)) {
|
|
1727
|
+
stores?.forEach((s) => getStoreAdm(s)?.mount(this, name));
|
|
1728
|
+
} else if (stores) {
|
|
1729
|
+
getStoreAdm(stores)?.mount(this, name);
|
|
1730
|
+
}
|
|
1731
|
+
});
|
|
1732
|
+
this.mounted = true;
|
|
1733
|
+
batch(() => this.proxy.storeDidMount?.());
|
|
1734
|
+
} catch (error) {
|
|
1735
|
+
if (error instanceof RangeError && /call stack/i.test(error.message)) {
|
|
1736
|
+
throw createCircularMountError(frame);
|
|
1572
1737
|
}
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1738
|
+
throw error;
|
|
1739
|
+
} finally {
|
|
1740
|
+
mountingStack.pop();
|
|
1741
|
+
}
|
|
1576
1742
|
}
|
|
1577
1743
|
unmount() {
|
|
1578
1744
|
this.proxy.storeWillUnmount?.();
|
|
@@ -1890,6 +2056,30 @@ function getSnapshotRefId(snapshot) {
|
|
|
1890
2056
|
}
|
|
1891
2057
|
return snapshot[keys[0]];
|
|
1892
2058
|
}
|
|
2059
|
+
function validateModelChildValue(value, propertyName) {
|
|
2060
|
+
if (value === null || value === void 0) {
|
|
2061
|
+
return;
|
|
2062
|
+
}
|
|
2063
|
+
if (value instanceof Model) {
|
|
2064
|
+
return;
|
|
2065
|
+
}
|
|
2066
|
+
if (Array.isArray(value)) {
|
|
2067
|
+
const invalidItem = value.find((item) => !(item instanceof Model));
|
|
2068
|
+
if (invalidItem !== void 0) {
|
|
2069
|
+
throw new Error(
|
|
2070
|
+
`r-state-tree: child property '${String(
|
|
2071
|
+
propertyName
|
|
2072
|
+
)}' must be a Model instance, an array of Model instances, or null/undefined. Found invalid array item: ${typeof invalidItem}`
|
|
2073
|
+
);
|
|
2074
|
+
}
|
|
2075
|
+
return;
|
|
2076
|
+
}
|
|
2077
|
+
throw new Error(
|
|
2078
|
+
`r-state-tree: child property '${String(
|
|
2079
|
+
propertyName
|
|
2080
|
+
)}' must be a Model instance, an array of Model instances, or null/undefined. Found: ${typeof value}`
|
|
2081
|
+
);
|
|
2082
|
+
}
|
|
1893
2083
|
class ModelAdministration extends PreactObjectAdministration {
|
|
1894
2084
|
static proxyTraps = Object.assign(
|
|
1895
2085
|
{},
|
|
@@ -1923,6 +2113,7 @@ class ModelAdministration extends PreactObjectAdministration {
|
|
|
1923
2113
|
return true;
|
|
1924
2114
|
}
|
|
1925
2115
|
case CommonCfgTypes.child: {
|
|
2116
|
+
validateModelChildValue(value, name);
|
|
1926
2117
|
if (Array.isArray(value)) {
|
|
1927
2118
|
adm.setModels(name, value);
|
|
1928
2119
|
return true;
|
|
@@ -1935,7 +2126,9 @@ class ModelAdministration extends PreactObjectAdministration {
|
|
|
1935
2126
|
adm.setId(name, value);
|
|
1936
2127
|
break;
|
|
1937
2128
|
}
|
|
1938
|
-
case ModelCfgTypes.state:
|
|
2129
|
+
case ModelCfgTypes.state:
|
|
2130
|
+
case ModelCfgTypes.stateShallow:
|
|
2131
|
+
case ModelCfgTypes.stateSignal: {
|
|
1939
2132
|
adm.setState(name, value);
|
|
1940
2133
|
break;
|
|
1941
2134
|
}
|
|
@@ -2029,6 +2222,7 @@ class ModelAdministration extends PreactObjectAdministration {
|
|
|
2029
2222
|
}
|
|
2030
2223
|
}
|
|
2031
2224
|
setModel(name, newModel) {
|
|
2225
|
+
validateModelChildValue(newModel, name);
|
|
2032
2226
|
const currentValue = this.proxy[name];
|
|
2033
2227
|
if (currentValue === newModel) {
|
|
2034
2228
|
return;
|
|
@@ -2048,6 +2242,7 @@ class ModelAdministration extends PreactObjectAdministration {
|
|
|
2048
2242
|
}
|
|
2049
2243
|
}
|
|
2050
2244
|
setModels(name, newModelsSource) {
|
|
2245
|
+
validateModelChildValue(newModelsSource, name);
|
|
2051
2246
|
const newModels = createObservableWithCustomAdministration(
|
|
2052
2247
|
[],
|
|
2053
2248
|
ChildModelsAdministration
|
|
@@ -2203,6 +2398,8 @@ class ModelAdministration extends PreactObjectAdministration {
|
|
|
2203
2398
|
return Object.keys(this.configuration).reduce((json, key) => {
|
|
2204
2399
|
switch (this.configuration[key].type) {
|
|
2205
2400
|
case ModelCfgTypes.state:
|
|
2401
|
+
case ModelCfgTypes.stateShallow:
|
|
2402
|
+
case ModelCfgTypes.stateSignal:
|
|
2206
2403
|
case ModelCfgTypes.id:
|
|
2207
2404
|
json[key] = clone(getSource(this.proxy[key]));
|
|
2208
2405
|
break;
|
|
@@ -2268,6 +2465,8 @@ class ModelAdministration extends PreactObjectAdministration {
|
|
|
2268
2465
|
const value = snapshot[key];
|
|
2269
2466
|
switch (type) {
|
|
2270
2467
|
case ModelCfgTypes.state:
|
|
2468
|
+
case ModelCfgTypes.stateShallow:
|
|
2469
|
+
case ModelCfgTypes.stateSignal:
|
|
2271
2470
|
this.proxy[key] = value;
|
|
2272
2471
|
break;
|
|
2273
2472
|
case ModelCfgTypes.modelRef:
|
|
@@ -2483,7 +2682,10 @@ const child = makeChildDecorator(childType);
|
|
|
2483
2682
|
const modelRef = makeChildDecorator(modelRefType);
|
|
2484
2683
|
const model = makeDecorator(modelType);
|
|
2485
2684
|
const id = makeDecorator(idType);
|
|
2486
|
-
const state = makeDecorator(stateType)
|
|
2685
|
+
const state = Object.assign(makeDecorator(stateType), {
|
|
2686
|
+
shallow: makeDecorator(stateShallowType),
|
|
2687
|
+
signal: makeDecorator(stateSignalType)
|
|
2688
|
+
});
|
|
2487
2689
|
export {
|
|
2488
2690
|
Model,
|
|
2489
2691
|
Observable,
|
|
@@ -25,6 +25,6 @@ export declare class StoreAdministration<StoreType extends Store = Store> extend
|
|
|
25
25
|
getContextValue<T>(contextId: symbol, provideSymbol: symbol, defaultValue: T | undefined, hasDefault: boolean): T;
|
|
26
26
|
private lookupContextValue;
|
|
27
27
|
reaction<T>(track: () => T, callback: (a: T) => void): () => void;
|
|
28
|
-
mount(parent?: StoreAdministration | null): void;
|
|
28
|
+
mount(parent?: StoreAdministration | null, childName?: PropertyKey): void;
|
|
29
29
|
unmount(): void;
|
|
30
30
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -34,6 +34,8 @@ export declare enum CommonCfgTypes {
|
|
|
34
34
|
}
|
|
35
35
|
export declare enum ModelCfgTypes {
|
|
36
36
|
state = "state",
|
|
37
|
+
stateShallow = "stateShallow",
|
|
38
|
+
stateSignal = "stateSignal",
|
|
37
39
|
id = "id",
|
|
38
40
|
modelRef = "modelRef"
|
|
39
41
|
}
|
|
@@ -42,6 +44,8 @@ export declare enum StoreCfgTypes {
|
|
|
42
44
|
}
|
|
43
45
|
export declare enum ObservableCfgTypes {
|
|
44
46
|
observable = "observable",
|
|
47
|
+
observableShallow = "observableShallow",
|
|
48
|
+
observableSignal = "observableSignal",
|
|
45
49
|
computed = "computed"
|
|
46
50
|
}
|
|
47
51
|
export type ConfigurationTypes = CommonCfgTypes | ModelCfgTypes | StoreCfgTypes | ObservableCfgTypes;
|
|
@@ -70,11 +74,15 @@ export declare const childType: ((childType: Function) => ConfigurationType) & {
|
|
|
70
74
|
type: CommonCfgTypes;
|
|
71
75
|
};
|
|
72
76
|
export declare const stateType: ConfigurationType;
|
|
77
|
+
export declare const stateShallowType: ConfigurationType;
|
|
78
|
+
export declare const stateSignalType: ConfigurationType;
|
|
73
79
|
export declare const modelRefType: ((childType: Function) => ConfigurationType) & {
|
|
74
80
|
type: ModelCfgTypes;
|
|
75
81
|
};
|
|
76
82
|
export declare const idType: ConfigurationType;
|
|
77
83
|
export declare const modelType: ConfigurationType;
|
|
78
84
|
export declare const observableType: ConfigurationType;
|
|
85
|
+
export declare const observableShallowType: ConfigurationType;
|
|
86
|
+
export declare const observableSignalType: ConfigurationType;
|
|
79
87
|
export declare const computedType: ConfigurationType;
|
|
80
88
|
export {};
|