r-state-tree 0.4.6 → 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.
@@ -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 observable(value, context) {
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
- this.parent = parent || null;
1567
- this.childStoreDataMap.forEach(({ value }) => {
1568
- const stores = value.get();
1569
- if (Array.isArray(stores)) {
1570
- stores?.forEach((s) => getStoreAdm(s)?.mount(this));
1571
- } else if (stores) {
1572
- getStoreAdm(stores)?.mount(this);
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
- this.mounted = true;
1576
- signalsCore.batch(() => this.proxy.storeDidMount?.());
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,10 +2683,9 @@ 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);
2488
- Object.defineProperty(exports, "ReadonlySignal", {
2489
- enumerable: true,
2490
- get: () => signalsCore.ReadonlySignal
2686
+ const state = Object.assign(makeDecorator(stateType), {
2687
+ shallow: makeDecorator(stateShallowType),
2688
+ signal: makeDecorator(stateSignalType)
2491
2689
  });
2492
2690
  Object.defineProperty(exports, "Signal", {
2493
2691
  enumerable: true,