r-state-tree 0.6.0 → 0.6.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/README.md CHANGED
@@ -355,66 +355,45 @@ class ListStore extends Store {
355
355
  - Models: model fields are shallow-reactive, but values are not auto-wrapped. For raw `@state` arrays/objects, prefer **reassignment** (immutability) so snapshots stay up to date. If you want in-place mutation (`push`, `splice`, `set`, etc.) to trigger updates and snapshot invalidation, store an `observable()` container (or `signal()`) in `@state`.
356
356
  - Stores: store fields are shallow-reactive. Use `observable()` containers (or `signal()`) when you want in-place mutations of nested values/collections to trigger updates.
357
357
 
358
- ## Observable classes
358
+ ### `Observable` base class
359
359
 
360
- For reactive class instances outside the Model/Store system, wrap state with `observable()`:
360
+ For class instances that need to be reactive, extending the `Observable` base class is the supported pattern. This ensures compatibility with ES `#private` fields and built-in brand checks because the observable is created in the base constructor, allowing derived field initializers (including `#private`) to run on the observable.
361
361
 
362
362
  ```ts
363
- import { observable, computed, effect } from "r-state-tree";
363
+ import { Observable, effect } from "r-state-tree";
364
364
 
365
- class Counter {
366
- state = observable({ count: 0, step: 1 });
365
+ class Counter extends Observable {
366
+ count = 0; // Public fields are automatically reactive
367
+ #internal = 0; // Private fields also work perfectly
367
368
 
368
- @computed get doubled() {
369
- return this.state.count * 2;
369
+ get total() {
370
+ return this.count + this.#internal;
370
371
  }
371
372
 
372
373
  increment() {
373
- this.state.count += this.state.step;
374
+ this.count++;
375
+ this.#internal++;
374
376
  }
375
377
  }
376
378
 
377
379
  const counter = new Counter();
378
380
 
381
+ // Use an effect to track and react to property changes
379
382
  effect(() => {
380
- console.log("Count:", counter.state.count, "Doubled:", counter.doubled);
383
+ console.log(`Visible: ${counter.count}, Total: ${counter.total}`);
381
384
  });
382
385
 
383
- counter.increment(); // Triggers the effect
384
- ```
385
-
386
- ### `Observable` base class
387
-
388
- For class instances that need to be reactive, extending the `Observable` base class is the supported pattern. This ensures compatibility with ES `#private` fields and built-in brand checks because the Proxy is created in the base constructor, allowing derived field initializers (including `#private`) to run on the Proxy.
389
-
390
- ```ts
391
- import { Observable, effect } from "r-state-tree";
392
-
393
- class Counter extends Observable {
394
- #count = 0; // Correctly works with #private
395
-
396
- get count() {
397
- return this.#count;
398
- }
399
-
400
- increment() {
401
- this.#count++;
402
- }
403
- }
404
-
405
- const counter = new Counter();
406
- effect(() => console.log(counter.count));
407
386
  counter.increment();
408
387
  ```
409
388
 
410
389
  > [!IMPORTANT] > **Why `extends Observable`?**
411
- > ES `#private` fields are brand-checked. If you proxy a class instance _after_ it has been created (post-hoc proxying), or if you return a proxy from a standard constructor, the private state is installed on the original `this`, but methods run with the Proxy as `this`, causing `TypeError: Cannot read private member`.
390
+ > ES `#private` fields are brand-checked. If you wrap a class instance with `observable()` _after_ it has been created (post-hoc wrapping), or if you return an observable from a standard constructor, the private state is installed on the original `this`, but methods run with the observable as `this`, causing `TypeError: Cannot read private member`.
412
391
  >
413
- > `extends Observable` solves this by returning the Proxy from `super()`, so derived classes initialize their private fields directly on the Proxy receiver.
392
+ > `extends Observable` solves this by returning the observable from `super()`, so derived classes initialize their private fields directly on the observable receiver.
414
393
 
415
394
  ### Limitations of `observable(instance)`
416
395
 
417
- The `observable()` function is selective about what it proxies. It wraps **supported containers** only:
396
+ The `observable()` function is selective about what it makes observable. It wraps **supported containers** only:
418
397
 
419
398
  - Plain objects
420
399
  - Arrays
@@ -425,10 +404,10 @@ The `observable()` function is selective about what it proxies. It wraps **suppo
425
404
 
426
405
  If you pass an arbitrary class instance, built-in (like `URL`, `RegExp`, `Promise`, or DOM objects), or frozen object to `observable()`, it will **return the object unchanged** and emit a warning in development mode.
427
406
 
428
- This design prevents “silent failure” where a proxied object appears to work but throws `TypeError` when accessing private members or internal slots. For your own classes, use `extends Observable`. For third-party or built-in objects, use composition:
407
+ This design prevents “silent failure” where an observable object appears to work but throws `TypeError` when accessing private members or internal slots. For your own classes, use `extends Observable`. For third-party or built-in objects, use composition:
429
408
 
430
409
  ```ts
431
- // ❌ Post-hoc proxying - returns raw URL, not a proxy
410
+ // ❌ Post-hoc wrapping - returns raw URL, not an observable
432
411
  const url = observable(new URL("..."));
433
412
 
434
413
  // ✅ Composition - wrap a container instead
@@ -520,19 +499,19 @@ Create reactive structures outside Stores/Models. Supported: Objects, Arrays, Ma
520
499
 
521
500
  - Track reads with `effect`/`reaction`. Observables are **shallow**: reads are tracked on the observable container, but nested object mutations do **not** trigger unless you explicitly wrap nested values with `observable()` (or use signals).
522
501
  - Access backing values via `source(value)`; check if something is reactive with `isObservable(value)`.
523
- - Rule of thumb: `source(...)` returns the backing data, **not** a sanitizer—it's only proxy-free if you didn't manually seed proxies into the backing source.
502
+ - Rule of thumb: `source(...)` returns the backing data, **not** a sanitizer—it's only observable-free if you didn't manually seed observables into the backing source.
524
503
  - Arrays: reading specific indices (`arr[i]`) or `length` tracks those; common mutators (`push/pop/shift/unshift/splice/reverse/sort/fill`) are reactive; non-index and symbol keys are not reactive.
525
504
 
526
505
  ### `source()` and structuredClone
527
506
 
528
- `source(x)` returns the backing value behind an observable proxy/container. It is **not** a “proxy stripper”.
507
+ `source(x)` returns the backing value behind an observable. It is **not** a “observable stripper”.
529
508
 
530
509
  **One-way rule (important):**
531
510
 
532
511
  - If you mutate through the observable wrapper (e.g. `obj.prop = observable(child)`), r-state-tree stores the **raw backing value** in `source(obj).prop` (unwrap-on-write).
533
- - If you manually mutate backing sources yourself (e.g. `source(obj).prop = observable(child)`), r-state-tree does **not** sanitize or rewrite your data. In that case, `source(...)` may contain proxies.
512
+ - If you manually mutate backing sources yourself (e.g. `source(obj).prop = observable(child)`), r-state-tree does **not** sanitize or rewrite your data. In that case, `source(...)` may contain observables.
534
513
 
535
- If you need to pass values to APIs that require cloneable data (e.g. `structuredClone`, `postMessage`), avoid seeding observable proxies into backing sources. Prefer reading via `source(...)` and keep your stored values plain/cloneable.
514
+ If you need to pass values to APIs that require cloneable data (e.g. `structuredClone`, `postMessage`), avoid seeding observables into backing sources. Prefer reading via `source(...)` and keep your stored values plain/cloneable.
536
515
 
537
516
  ```ts
538
517
  import { observable, effect, computed, reaction } from "r-state-tree";
@@ -586,7 +565,7 @@ state.count++;
586
565
  Native methods that return copies of a collection—such as `Array.prototype.slice`, `filter`, and `concat`, or `Set.prototype.union` and `intersection`—behave according to the **Explicit Architecture**:
587
566
 
588
567
  - **Raw Return**: The returned container is a plain JavaScript object (raw, non-observable).
589
- - **Identity Preservation**: Each element in the returned container maintains the same identity it had in the observable source. If an element was a proxy (because it was explicitly owned), it remains a proxy in the raw result.
568
+ - **Identity Preservation**: Each element in the returned container maintains the same identity it had in the observable source. If an element was an observable (because it was explicitly owned), it remains an observable in the raw result.
590
569
 
591
570
  This ensures that "derived" state is not automatically made reactive, while still allowing observers to maintain reference stability with existing objects in your state tree.
592
571
 
@@ -599,7 +578,7 @@ const sliced = arr.slice();
599
578
  isObservable(sliced); // false
600
579
 
601
580
  // Identity is preserved
602
- sliced[0] === item; // true (same proxy identity)
581
+ sliced[0] === item; // true (same observable identity)
603
582
 
604
583
  // If you want the result to be reactive, wrap it explicitly
605
584
  const reactiveSlice = observable(arr.slice());
@@ -634,8 +613,8 @@ tree.user.tags[0] = "superadmin"; // triggers
634
613
  tree.newProp = { foo: 1 };
635
614
  isObservable(tree.newProp); // false — not wrapped
636
615
 
637
- // Source is proxy-free and clonable *as long as you don't manually seed proxies*
638
- // into backing data structures. r-state-tree unwraps proxies on writes performed
616
+ // Source is observable-free and clonable *as long as you don't manually seed observables*
617
+ // into backing data structures. r-state-tree unwraps observables on writes performed
639
618
  // through observable containers, but it does not sanitize user-mutated backing sources.
640
619
  const snapshot = structuredClone(source(tree));
641
620
  ```
@@ -1127,7 +1106,7 @@ const off = onSnapshot(m, (snap) =>
1127
1106
  - Forgetting stable keys for `@child` arrays causes identity churn.
1128
1107
  - Assuming **deep** reactivity: nested plain objects/arrays are not reactive unless you explicitly wrap them (or use `toObservableTree` for initial hydration).
1129
1108
  - Mutating raw `@state` arrays/objects in place (`push`, `obj.x = 1`) and expecting snapshots to update. Snapshots are memoized; use reassignment or store `observable()` containers / `signal()` values in `@state`.
1130
- - Passing observable proxies to third‑party APIs that expect cloneable/serializable values (e.g. `structuredClone`). Use `source(value)` to get the backing value. It will be proxy-free for values written via r-state-tree’s observable APIs (unwrap-on-write), but `source(...)` is **not** guaranteed proxy-free if you manually seed proxies into backing sources.
1109
+ - Passing observables to third‑party APIs that expect cloneable/serializable values (e.g. `structuredClone`). Use `source(value)` to get the backing value. It will be observable-free for values written via r-state-tree’s observable APIs (unwrap-on-write), but `source(...)` is **not** guaranteed observable-free if you manually seed observables into backing sources.
1131
1110
  - Creating child stores in constructors: `@child` must be on getters so identity and lifecycle can be managed by the framework.
1132
1111
  - 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.
1133
1112
 
@@ -16,11 +16,9 @@ export declare class ModelAdministration extends PreactObjectAdministration<any>
16
16
  private modelsTraceUnsub;
17
17
  private writeInProgress;
18
18
  private computedSnapshot;
19
- private snapshotAtom;
20
19
  private snapshotMap;
21
20
  private contextCache;
22
21
  parentName: PropertyKey | null;
23
- private observeSnapshotValue;
24
22
  get parent(): ModelAdministration | null;
25
23
  set parent(value: ModelAdministration | null);
26
24
  setConfiguration(configurationGetter: () => ModelConfiguration<any>): void;
@@ -4,8 +4,9 @@ import { SignalMap } from "./internal/NodeMap";
4
4
  export declare class ArrayAdministration<T> extends Administration<T[]> {
5
5
  valuesMap: SignalMap<number>;
6
6
  keysAtom: AtomNode;
7
- private explicitObservables;
8
- private syncExplicitObservablesFromSource;
7
+ private observableSources;
8
+ private rawSources;
9
+ private trackValue;
9
10
  static proxyTraps: ProxyHandler<Array<unknown>>;
10
11
  static methods: Partial<{
11
12
  [K in keyof typeof Array.prototype as (typeof Array.prototype)[K] extends Function ? K : never]: (typeof Array.prototype)[K];
@@ -7,7 +7,7 @@ export declare class ObjectAdministration<T extends object> extends Administrati
7
7
  hasMap: AtomMap<PropertyKey>;
8
8
  valuesMap: SignalMap<PropertyKey>;
9
9
  computedMap: Map<PropertyKey, ComputedNode<T[keyof T]>>;
10
- types: Map<PropertyKey, PropertyType | null>;
10
+ types: Map<PropertyKey, PropertyType>;
11
11
  isWriting: boolean;
12
12
  static proxyTraps: ProxyHandler<object>;
13
13
  constructor(source?: T);
@@ -72,14 +72,7 @@ function getPropertyType(key, obj) {
72
72
  if (descriptor?.value && typeof descriptor.value === "function") {
73
73
  return "action";
74
74
  }
75
- const isAccessor = descriptor && (typeof descriptor.get === "function" || typeof descriptor.set === "function");
76
75
  if (!hasMetadata) {
77
- if (descriptor?.get) {
78
- return "computed";
79
- }
80
- if (isAccessor) {
81
- return null;
82
- }
83
76
  return "observable";
84
77
  }
85
78
  const metadata = obj.constructor[Symbol.metadata];
@@ -96,7 +89,7 @@ function getPropertyType(key, obj) {
96
89
  return "observable";
97
90
  }
98
91
  }
99
- return isAccessor ? null : "observable";
92
+ return "observable";
100
93
  }
101
94
  function getPropertyDescriptor$1(obj, key) {
102
95
  let node = obj;
@@ -315,9 +308,7 @@ class ObjectAdministration extends Administration {
315
308
  return Reflect.get(this.source, key, this.proxy);
316
309
  }
317
310
  set(key, value) {
318
- return signalsCore.batch(() => {
319
- return Reflect.set(this.source, key, value, this.proxy);
320
- });
311
+ return signalsCore.batch(() => Reflect.set(this.source, key, value, this.proxy));
321
312
  }
322
313
  getComputed(key) {
323
314
  if (!this.computedMap) this.computedMap = /* @__PURE__ */ new Map();
@@ -360,55 +351,42 @@ class ObjectAdministration extends Administration {
360
351
  }
361
352
  read(key, receiver = this.proxy) {
362
353
  const type = this.getType(key);
363
- if (type === null) {
354
+ if (type === "computed") {
355
+ if (receiver === this.proxy) {
356
+ return this.callComputed(key);
357
+ }
358
+ this.atom.reportObserved();
364
359
  return Reflect.get(this.source, key, receiver);
365
360
  }
366
- switch (type) {
367
- case "observable":
368
- case "action": {
369
- if (key in this.source) {
370
- this.valuesMap.reportObserved(key, this.source[key]);
371
- }
372
- this.atom.reportObserved();
373
- if (this.atom.observing) {
374
- this.hasMap.reportObserved(key);
375
- }
376
- if (type === "observable") {
377
- const value = Reflect.get(this.source, key, receiver);
378
- const shouldWrap = this.explicitObservables.has(key) || isObservable(value);
379
- if (shouldWrap && value && typeof value === "object" && !Object.isFrozen(value)) {
380
- const desc = getPropertyDescriptor$1(this.source, key);
381
- if (desc && !desc.configurable && !desc.writable) {
382
- return value;
383
- }
384
- const existingAdm = getAdministration(value);
385
- if (existingAdm) {
386
- return existingAdm.proxy;
387
- }
388
- }
361
+ if (key in this.source) {
362
+ this.valuesMap.reportObserved(key, this.source[key]);
363
+ }
364
+ this.atom.reportObserved();
365
+ if (this.atom.observing) {
366
+ this.hasMap.reportObserved(key);
367
+ }
368
+ if (type === "observable") {
369
+ const value = Reflect.get(this.source, key, receiver);
370
+ const shouldWrap = this.explicitObservables.has(key) || isObservable(value);
371
+ if (shouldWrap && value && typeof value === "object" && !Object.isFrozen(value)) {
372
+ const desc = getPropertyDescriptor$1(this.source, key);
373
+ if (desc && !desc.configurable && !desc.writable) {
389
374
  return value;
390
375
  }
391
- return getAction(
392
- Reflect.get(this.source, key, receiver)
393
- );
394
- }
395
- case "computed": {
396
- if (receiver === this.proxy) {
397
- return this.callComputed(key);
376
+ const existingAdm = getAdministration(value);
377
+ if (existingAdm) {
378
+ return existingAdm.proxy;
398
379
  }
399
- this.atom.reportObserved();
400
- return Reflect.get(this.source, key, receiver);
401
380
  }
402
- default:
403
- throw new Error(`unknown type passed to configure`);
381
+ return value;
404
382
  }
383
+ return getAction(
384
+ Reflect.get(this.source, key, receiver)
385
+ );
405
386
  }
406
387
  explicitObservables = /* @__PURE__ */ new Set();
407
388
  write(key, newValue) {
408
389
  const type = this.getType(key);
409
- if (type === null) {
410
- return this.set(key, newValue);
411
- }
412
390
  if (type === "computed") {
413
391
  return signalsCore.batch(() => this.set(key, newValue));
414
392
  }
@@ -592,8 +570,24 @@ function createListener(callback) {
592
570
  };
593
571
  return listener;
594
572
  }
573
+ function internalCreateComputed(fn) {
574
+ const version = signalsCore.signal(0);
575
+ const c = signalsCore.computed(
576
+ () => {
577
+ version.value;
578
+ return fn();
579
+ },
580
+ {
581
+ unwatched() {
582
+ version.value++;
583
+ }
584
+ }
585
+ );
586
+ return { c, version };
587
+ }
595
588
  function createComputed(fn, context = null) {
596
- const c = signalsCore.computed(context ? fn.bind(context) : fn);
589
+ const bound = context ? fn.bind(context) : fn;
590
+ const { c } = internalCreateComputed(bound);
597
591
  return {
598
592
  node: c,
599
593
  get() {
@@ -602,6 +596,7 @@ function createComputed(fn, context = null) {
602
596
  clear: () => {
603
597
  signalsCore.untracked(() => {
604
598
  c._value = void 0;
599
+ c._globalVersion = -1;
605
600
  });
606
601
  }
607
602
  };
@@ -617,7 +612,7 @@ function computed(value, context) {
617
612
  context.metadata[context.name] = { type: "computed" };
618
613
  return value;
619
614
  }
620
- return signalsCore.computed(value);
615
+ return internalCreateComputed(value).c;
621
616
  }
622
617
  function source(obj) {
623
618
  return getSource(obj);
@@ -683,17 +678,7 @@ class CollectionAdministration extends Administration {
683
678
  return collectionMethods[name];
684
679
  }
685
680
  if (typeof val === "function") {
686
- return function() {
687
- if (process.env.NODE_ENV !== "production") {
688
- console.warn(
689
- `r-state-tree: Calling uninstrumented collection method "${String(
690
- name
691
- )}". Reactivity is not guaranteed for this call.`
692
- );
693
- }
694
- const result = val.apply(adm.source, arguments);
695
- return result === adm.source ? adm.proxy : result;
696
- };
681
+ return val;
697
682
  }
698
683
  return val;
699
684
  }
@@ -819,9 +804,6 @@ class CollectionAdministration extends Administration {
819
804
  const target = getSource(value);
820
805
  if (isObservable(value)) {
821
806
  this.trackExplicitObservable(target);
822
- } else {
823
- this.untrackExplicitObservable(target);
824
- this.untrackExplicitObservable(value);
825
807
  }
826
808
  if (!this.hasEntry(value)) {
827
809
  this.source.add(target);
@@ -1054,15 +1036,40 @@ function isArrayIndexKey(key) {
1054
1036
  return Number.isInteger(num) && num >= 0 && num < 4294967295 && // 2^32 - 1
1055
1037
  String(num) === keyStr;
1056
1038
  }
1039
+ function reportConflict() {
1040
+ if (process.env.NODE_ENV !== "production") {
1041
+ throw new Error(
1042
+ "r-state-tree: Cannot assign the same object as both observable and raw source in the same array. The value will be treated as observable."
1043
+ );
1044
+ }
1045
+ }
1057
1046
  class ArrayAdministration extends Administration {
1058
1047
  valuesMap;
1059
1048
  keysAtom;
1060
- explicitObservables = /* @__PURE__ */ new Set();
1061
- syncExplicitObservablesFromSource() {
1062
- for (let i = 0; i < this.source.length; i++) {
1063
- const value = this.source[i];
1049
+ /**
1050
+ * Parent-based ownership tracking: sources assigned as observable.
1051
+ * Unlike index-based tracking, this doesn't require updates on reorder ops.
1052
+ */
1053
+ observableSources = /* @__PURE__ */ new WeakSet();
1054
+ rawSources = /* @__PURE__ */ new WeakSet();
1055
+ /**
1056
+ * Track a value being assigned to this array.
1057
+ * Detects conflicts if same source is assigned as both observable and raw.
1058
+ */
1059
+ trackValue(value) {
1060
+ if (value && typeof value === "object") {
1061
+ const source2 = getSource(value);
1064
1062
  if (isObservable(value)) {
1065
- this.explicitObservables.add(i);
1063
+ if (this.rawSources.has(source2)) {
1064
+ reportConflict();
1065
+ }
1066
+ this.observableSources.add(source2);
1067
+ } else {
1068
+ if (this.observableSources.has(source2)) {
1069
+ reportConflict();
1070
+ } else {
1071
+ this.rawSources.add(source2);
1072
+ }
1066
1073
  }
1067
1074
  }
1068
1075
  }
@@ -1119,7 +1126,6 @@ class ArrayAdministration extends Administration {
1119
1126
  if (result && had && isIndex) {
1120
1127
  const index = Number(name);
1121
1128
  adm.onArrayChanged(true, index, 1);
1122
- adm.explicitObservables.delete(index);
1123
1129
  }
1124
1130
  return result;
1125
1131
  },
@@ -1145,16 +1151,12 @@ class ArrayAdministration extends Administration {
1145
1151
  static methods = {
1146
1152
  fill(value, start, end) {
1147
1153
  const adm = getAdministration(this);
1148
- const shouldTrack = isObservable(value);
1154
+ adm.trackValue(value);
1149
1155
  const targetValue = getSource(value);
1150
1156
  adm.source.fill(targetValue, start, end);
1151
1157
  const length = adm.source.length;
1152
1158
  const from = start == null ? 0 : start < 0 ? Math.max(length + start, 0) : start;
1153
1159
  const to = end == null ? length : end < 0 ? Math.max(length + end, 0) : Math.min(end, length);
1154
- for (let i = from; i < to; i++) {
1155
- if (shouldTrack) adm.explicitObservables.add(i);
1156
- else adm.explicitObservables.delete(i);
1157
- }
1158
1160
  if (from < to) {
1159
1161
  adm.onArrayChanged(false, from, to - from);
1160
1162
  }
@@ -1193,41 +1195,24 @@ class ArrayAdministration extends Administration {
1193
1195
  },
1194
1196
  reverse() {
1195
1197
  const adm = getAdministration(this);
1196
- adm.syncExplicitObservablesFromSource();
1197
- const flags = new Array(adm.source.length);
1198
- for (let i = 0; i < flags.length; i++) {
1199
- flags[i] = adm.explicitObservables.has(i);
1200
- }
1201
1198
  adm.source.reverse();
1202
- flags.reverse();
1203
- adm.explicitObservables.clear();
1204
- for (let i = 0; i < flags.length; i++) {
1205
- if (flags[i]) adm.explicitObservables.add(i);
1206
- }
1207
1199
  adm.onArrayChanged(false, 0, adm.source.length);
1208
1200
  return this;
1209
1201
  },
1210
1202
  sort(compareFn) {
1211
1203
  const adm = getAdministration(this);
1212
- adm.syncExplicitObservablesFromSource();
1213
- const pairs = new Array(adm.source.length);
1214
- for (let i = 0; i < adm.source.length; i++) {
1215
- pairs[i] = {
1216
- value: this[i],
1217
- stored: adm.source[i],
1218
- observed: adm.explicitObservables.has(i)
1219
- };
1220
- }
1204
+ const pairs = adm.source.map((stored, i) => ({
1205
+ value: this[i],
1206
+ stored
1207
+ }));
1221
1208
  const comparator = compareFn ?? ((a, b) => {
1222
1209
  const as = String(a);
1223
1210
  const bs = String(b);
1224
1211
  return as < bs ? -1 : as > bs ? 1 : 0;
1225
1212
  });
1226
1213
  pairs.sort((a, b) => comparator(a.value, b.value));
1227
- adm.explicitObservables.clear();
1228
1214
  for (let i = 0; i < pairs.length; i++) {
1229
1215
  adm.source[i] = pairs[i].stored;
1230
- if (pairs[i].observed) adm.explicitObservables.add(i);
1231
1216
  }
1232
1217
  adm.onArrayChanged(false, 0, adm.source.length);
1233
1218
  return this;
@@ -1243,17 +1228,7 @@ class ArrayAdministration extends Administration {
1243
1228
  flat: createCopyMethod("flat"),
1244
1229
  copyWithin(target, start, end) {
1245
1230
  const adm = getAdministration(this);
1246
- adm.syncExplicitObservablesFromSource();
1247
- const flags = new Array(adm.source.length);
1248
- for (let i = 0; i < flags.length; i++) {
1249
- flags[i] = adm.explicitObservables.has(i);
1250
- }
1251
1231
  adm.source.copyWithin(target, start, end);
1252
- flags.copyWithin(target, start, end);
1253
- adm.explicitObservables.clear();
1254
- for (let i = 0; i < flags.length; i++) {
1255
- if (flags[i]) adm.explicitObservables.add(i);
1256
- }
1257
1232
  adm.onArrayChanged(false, 0, adm.source.length);
1258
1233
  return this;
1259
1234
  },
@@ -1287,23 +1262,20 @@ class ArrayAdministration extends Administration {
1287
1262
  _getEffectiveValue(index) {
1288
1263
  const value = this.source[index];
1289
1264
  if (value && typeof value === "object" && !Object.isFrozen(value)) {
1290
- if (this.explicitObservables.has(index)) {
1291
- const existingAdm = getAdministration(value);
1292
- if (existingAdm) {
1293
- if (existingAdm.proxy !== value) {
1294
- const desc = Object.getOwnPropertyDescriptor(this.source, index);
1295
- if (desc && !desc.configurable && !desc.writable) {
1296
- if (process.env.NODE_ENV !== "production") {
1297
- console.warn(
1298
- `r-state-tree: cannot return an observable proxy for arr[${index}] because it is a non-configurable, non-writable data property; returning the raw value to uphold Proxy invariants.`
1299
- );
1300
- }
1301
- this.explicitObservables.delete(index);
1302
- return value;
1265
+ if (this.observableSources.has(value)) {
1266
+ const adm = getAdministration(value);
1267
+ if (adm.proxy !== value) {
1268
+ const desc = Object.getOwnPropertyDescriptor(this.source, index);
1269
+ if (desc && !desc.configurable && !desc.writable) {
1270
+ if (process.env.NODE_ENV !== "production") {
1271
+ console.warn(
1272
+ `r-state-tree: cannot return an observable proxy for arr[${index}] because it is a non-configurable, non-writable data property; returning the raw value to uphold Proxy invariants.`
1273
+ );
1303
1274
  }
1275
+ return value;
1304
1276
  }
1305
- return existingAdm.proxy;
1306
1277
  }
1278
+ return adm.proxy;
1307
1279
  }
1308
1280
  }
1309
1281
  return value;
@@ -1312,23 +1284,17 @@ class ArrayAdministration extends Administration {
1312
1284
  const values = this.source;
1313
1285
  const targetValue = getSource(newValue);
1314
1286
  const oldLength = values.length;
1287
+ this.trackValue(newValue);
1315
1288
  let changed = true;
1316
1289
  if (index < oldLength) {
1317
1290
  const oldValue = values[index];
1318
- const oldExplicit = this.explicitObservables.has(index);
1319
- const newExplicit = isObservable(newValue);
1320
- changed = !isObservable(oldValue) && oldExplicit !== newExplicit || (isObservable(oldValue) ? newValue !== oldValue : targetValue !== oldValue);
1291
+ changed = targetValue !== oldValue;
1321
1292
  }
1322
1293
  if (!changed) return true;
1323
1294
  const result = Reflect.set(values, index, targetValue);
1324
1295
  if (!result) return false;
1325
1296
  const newLength = values.length;
1326
1297
  const lengthChanged = newLength !== oldLength;
1327
- if (isObservable(newValue)) {
1328
- this.explicitObservables.add(index);
1329
- } else {
1330
- this.explicitObservables.delete(index);
1331
- }
1332
1298
  this.onArrayChanged(lengthChanged, index, 1);
1333
1299
  return true;
1334
1300
  }
@@ -1349,15 +1315,6 @@ class ArrayAdministration extends Administration {
1349
1315
  const result = Reflect.set(this.source, "length", newLength);
1350
1316
  if (!result) return false;
1351
1317
  if (newLength < currentLength) {
1352
- const toRemove = [];
1353
- for (const index of this.explicitObservables) {
1354
- if (index >= newLength) {
1355
- toRemove.push(index);
1356
- }
1357
- }
1358
- for (const index of toRemove) {
1359
- this.explicitObservables.delete(index);
1360
- }
1361
1318
  this.onArrayChanged(true, newLength, currentLength - newLength);
1362
1319
  } else {
1363
1320
  this.onArrayChanged(true, currentLength, newLength - currentLength);
@@ -1367,13 +1324,10 @@ class ArrayAdministration extends Administration {
1367
1324
  spliceWithArray(index, deleteCount, newItems) {
1368
1325
  const length = this.source.length;
1369
1326
  const newTargetItems = [];
1370
- const newObservableIndices = [];
1371
1327
  if (newItems) {
1372
1328
  for (let i = 0; i < newItems.length; i++) {
1373
1329
  newTargetItems[i] = getSource(newItems[i]);
1374
- if (isObservable(newItems[i])) {
1375
- newObservableIndices.push(i);
1376
- }
1330
+ this.trackValue(newItems[i]);
1377
1331
  }
1378
1332
  }
1379
1333
  if (index === void 0) index = 0;
@@ -1386,31 +1340,10 @@ class ArrayAdministration extends Administration {
1386
1340
  for (let i = index; i < index + deleteCount; i++) {
1387
1341
  removedItems.push(this._getEffectiveValue(i));
1388
1342
  }
1389
- for (let i = index; i < index + deleteCount; i++) {
1390
- this.explicitObservables.delete(i);
1391
- }
1392
- const shift = (newItems?.length ?? 0) - deleteCount;
1393
- if (shift !== 0) {
1394
- const newExplicitObservables = /* @__PURE__ */ new Set();
1395
- for (const idx of this.explicitObservables) {
1396
- if (idx < index) {
1397
- newExplicitObservables.add(idx);
1398
- } else if (idx >= index + deleteCount) {
1399
- newExplicitObservables.add(idx + shift);
1400
- }
1401
- }
1402
- this.explicitObservables.clear();
1403
- for (const idx of newExplicitObservables) {
1404
- this.explicitObservables.add(idx);
1405
- }
1406
- }
1407
- for (const relativeIdx of newObservableIndices) {
1408
- this.explicitObservables.add(index + relativeIdx);
1409
- }
1410
1343
  this.spliceItemsIntoValues(index, deleteCount, newTargetItems);
1411
1344
  if (deleteCount !== 0 || newTargetItems.length !== 0) {
1412
- const shift2 = (newItems?.length ?? 0) - deleteCount;
1413
- const reindexing = shift2 !== 0 && index + deleteCount < length;
1345
+ const shift = (newItems?.length ?? 0) - deleteCount;
1346
+ const reindexing = shift !== 0 && index + deleteCount < length;
1414
1347
  const count = reindexing ? Number.POSITIVE_INFINITY : Math.max(deleteCount ?? 0, newItems?.length ?? 0);
1415
1348
  this.onArrayChanged(length !== this.source.length, index, count);
1416
1349
  }
@@ -2437,19 +2370,7 @@ function getIdKey(Ctor) {
2437
2370
  function getModelRefSnapshot(modelRef2) {
2438
2371
  const Ctor = Object.getPrototypeOf(modelRef2).constructor;
2439
2372
  const idKey = getIdKey(Ctor);
2440
- if (!idKey) return null;
2441
- let id2 = getIdentifier(modelRef2);
2442
- if (id2 === void 0) {
2443
- const source2 = getSource(modelRef2);
2444
- id2 = getIdentifier(source2);
2445
- if (id2 === void 0) {
2446
- const adm = getAdministration(modelRef2);
2447
- if (adm && adm.proxy !== modelRef2) {
2448
- id2 = getIdentifier(adm.proxy);
2449
- }
2450
- }
2451
- }
2452
- return { [idKey]: id2 };
2373
+ return idKey ? { [idKey]: getIdentifier(modelRef2) } : null;
2453
2374
  }
2454
2375
  function getSnapshotId(snapshot, Ctor) {
2455
2376
  const idKey = getIdKey(Ctor);
@@ -2573,60 +2494,9 @@ class ModelAdministration extends PreactObjectAdministration {
2573
2494
  modelsTraceUnsub = /* @__PURE__ */ new Map();
2574
2495
  writeInProgress = /* @__PURE__ */ new Set();
2575
2496
  computedSnapshot;
2576
- snapshotAtom = createAtom();
2577
2497
  snapshotMap = /* @__PURE__ */ new Map();
2578
2498
  contextCache = /* @__PURE__ */ new Map();
2579
2499
  parentName = null;
2580
- /**
2581
- * `@state` fields are shallow-reactive (property-level assignment triggers reactivity)
2582
- * and participate in snapshots. Values stored in `@state` are not deep-wrapped.
2583
- *
2584
- * This helper walks a value and:
2585
- * - observes any observable containers found (proxy or source) so in-place mutations
2586
- * invalidate `getSnapshot()` / `toSnapshot()`
2587
- * - avoids proxy traversal by preferring `getSource(...)`
2588
- * - guards against cycles via `WeakSet`
2589
- */
2590
- observeSnapshotValue(value, seen) {
2591
- if (value == null) return;
2592
- if (typeof value !== "object") return;
2593
- if (!seen) seen = /* @__PURE__ */ new WeakSet();
2594
- const obj = value;
2595
- if (seen.has(obj)) return;
2596
- seen.add(obj);
2597
- const adm = getAdministration(obj);
2598
- if (adm) {
2599
- adm.reportObserved();
2600
- }
2601
- const src = getSource(value);
2602
- if (src && typeof src === "object" && src !== value) {
2603
- this.observeSnapshotValue(src, seen);
2604
- return;
2605
- }
2606
- if (Array.isArray(value)) {
2607
- for (let i = 0; i < value.length; i++) {
2608
- this.observeSnapshotValue(value[i], seen);
2609
- }
2610
- return;
2611
- }
2612
- if (value instanceof Map) {
2613
- value.forEach((v, k) => {
2614
- this.observeSnapshotValue(k, seen);
2615
- this.observeSnapshotValue(v, seen);
2616
- });
2617
- return;
2618
- }
2619
- if (value instanceof Set) {
2620
- value.forEach((v) => this.observeSnapshotValue(v, seen));
2621
- return;
2622
- }
2623
- if (value instanceof Date) {
2624
- return;
2625
- }
2626
- for (const key of Object.keys(value)) {
2627
- this.observeSnapshotValue(value[key], seen);
2628
- }
2629
- }
2630
2500
  get parent() {
2631
2501
  this.parentAtom.reportObserved();
2632
2502
  return this._parent;
@@ -2662,7 +2532,6 @@ class ModelAdministration extends PreactObjectAdministration {
2662
2532
  value,
2663
2533
  this.proxy
2664
2534
  );
2665
- this.snapshotAtom.reportChanged();
2666
2535
  });
2667
2536
  }
2668
2537
  }
@@ -2858,9 +2727,7 @@ class ModelAdministration extends PreactObjectAdministration {
2858
2727
  return Object.keys(this.configuration).reduce((json, key) => {
2859
2728
  switch (this.configuration[key].type) {
2860
2729
  case ModelCfgTypes.state: {
2861
- const value = this.proxy[key];
2862
- this.observeSnapshotValue(value);
2863
- json[key] = clone(getSource(value), key);
2730
+ json[key] = clone(this.proxy[key], key);
2864
2731
  break;
2865
2732
  }
2866
2733
  case ModelCfgTypes.id:
@@ -3003,7 +2870,6 @@ class ModelAdministration extends PreactObjectAdministration {
3003
2870
  getSnapshot() {
3004
2871
  if (!this.computedSnapshot) {
3005
2872
  this.computedSnapshot = createComputed(() => {
3006
- this.snapshotAtom.reportObserved();
3007
2873
  const json = this.toJSON();
3008
2874
  configMap.set(json, this.configuration);
3009
2875
  return json;
@@ -1,4 +1,4 @@
1
- import { batch, signal as signal$1, Signal, computed as computed$1, untracked, effect } from "@preact/signals-core";
1
+ import { batch, signal as signal$1, Signal, untracked, effect, computed as computed$1 } from "@preact/signals-core";
2
2
  import { Signal as Signal2, batch as batch2, effect as effect2, untracked as untracked2 } from "@preact/signals-core";
3
3
  var lib = {};
4
4
  var hasRequiredLib;
@@ -71,14 +71,7 @@ function getPropertyType(key, obj) {
71
71
  if (descriptor?.value && typeof descriptor.value === "function") {
72
72
  return "action";
73
73
  }
74
- const isAccessor = descriptor && (typeof descriptor.get === "function" || typeof descriptor.set === "function");
75
74
  if (!hasMetadata) {
76
- if (descriptor?.get) {
77
- return "computed";
78
- }
79
- if (isAccessor) {
80
- return null;
81
- }
82
75
  return "observable";
83
76
  }
84
77
  const metadata = obj.constructor[Symbol.metadata];
@@ -95,7 +88,7 @@ function getPropertyType(key, obj) {
95
88
  return "observable";
96
89
  }
97
90
  }
98
- return isAccessor ? null : "observable";
91
+ return "observable";
99
92
  }
100
93
  function getPropertyDescriptor$1(obj, key) {
101
94
  let node = obj;
@@ -314,9 +307,7 @@ class ObjectAdministration extends Administration {
314
307
  return Reflect.get(this.source, key, this.proxy);
315
308
  }
316
309
  set(key, value) {
317
- return batch(() => {
318
- return Reflect.set(this.source, key, value, this.proxy);
319
- });
310
+ return batch(() => Reflect.set(this.source, key, value, this.proxy));
320
311
  }
321
312
  getComputed(key) {
322
313
  if (!this.computedMap) this.computedMap = /* @__PURE__ */ new Map();
@@ -359,55 +350,42 @@ class ObjectAdministration extends Administration {
359
350
  }
360
351
  read(key, receiver = this.proxy) {
361
352
  const type = this.getType(key);
362
- if (type === null) {
353
+ if (type === "computed") {
354
+ if (receiver === this.proxy) {
355
+ return this.callComputed(key);
356
+ }
357
+ this.atom.reportObserved();
363
358
  return Reflect.get(this.source, key, receiver);
364
359
  }
365
- switch (type) {
366
- case "observable":
367
- case "action": {
368
- if (key in this.source) {
369
- this.valuesMap.reportObserved(key, this.source[key]);
370
- }
371
- this.atom.reportObserved();
372
- if (this.atom.observing) {
373
- this.hasMap.reportObserved(key);
374
- }
375
- if (type === "observable") {
376
- const value = Reflect.get(this.source, key, receiver);
377
- const shouldWrap = this.explicitObservables.has(key) || isObservable(value);
378
- if (shouldWrap && value && typeof value === "object" && !Object.isFrozen(value)) {
379
- const desc = getPropertyDescriptor$1(this.source, key);
380
- if (desc && !desc.configurable && !desc.writable) {
381
- return value;
382
- }
383
- const existingAdm = getAdministration(value);
384
- if (existingAdm) {
385
- return existingAdm.proxy;
386
- }
387
- }
360
+ if (key in this.source) {
361
+ this.valuesMap.reportObserved(key, this.source[key]);
362
+ }
363
+ this.atom.reportObserved();
364
+ if (this.atom.observing) {
365
+ this.hasMap.reportObserved(key);
366
+ }
367
+ if (type === "observable") {
368
+ const value = Reflect.get(this.source, key, receiver);
369
+ const shouldWrap = this.explicitObservables.has(key) || isObservable(value);
370
+ if (shouldWrap && value && typeof value === "object" && !Object.isFrozen(value)) {
371
+ const desc = getPropertyDescriptor$1(this.source, key);
372
+ if (desc && !desc.configurable && !desc.writable) {
388
373
  return value;
389
374
  }
390
- return getAction(
391
- Reflect.get(this.source, key, receiver)
392
- );
393
- }
394
- case "computed": {
395
- if (receiver === this.proxy) {
396
- return this.callComputed(key);
375
+ const existingAdm = getAdministration(value);
376
+ if (existingAdm) {
377
+ return existingAdm.proxy;
397
378
  }
398
- this.atom.reportObserved();
399
- return Reflect.get(this.source, key, receiver);
400
379
  }
401
- default:
402
- throw new Error(`unknown type passed to configure`);
380
+ return value;
403
381
  }
382
+ return getAction(
383
+ Reflect.get(this.source, key, receiver)
384
+ );
404
385
  }
405
386
  explicitObservables = /* @__PURE__ */ new Set();
406
387
  write(key, newValue) {
407
388
  const type = this.getType(key);
408
- if (type === null) {
409
- return this.set(key, newValue);
410
- }
411
389
  if (type === "computed") {
412
390
  return batch(() => this.set(key, newValue));
413
391
  }
@@ -591,8 +569,24 @@ function createListener(callback) {
591
569
  };
592
570
  return listener;
593
571
  }
572
+ function internalCreateComputed(fn) {
573
+ const version = signal$1(0);
574
+ const c = computed$1(
575
+ () => {
576
+ version.value;
577
+ return fn();
578
+ },
579
+ {
580
+ unwatched() {
581
+ version.value++;
582
+ }
583
+ }
584
+ );
585
+ return { c, version };
586
+ }
594
587
  function createComputed(fn, context = null) {
595
- const c = computed$1(context ? fn.bind(context) : fn);
588
+ const bound = context ? fn.bind(context) : fn;
589
+ const { c } = internalCreateComputed(bound);
596
590
  return {
597
591
  node: c,
598
592
  get() {
@@ -601,6 +595,7 @@ function createComputed(fn, context = null) {
601
595
  clear: () => {
602
596
  untracked(() => {
603
597
  c._value = void 0;
598
+ c._globalVersion = -1;
604
599
  });
605
600
  }
606
601
  };
@@ -616,7 +611,7 @@ function computed(value, context) {
616
611
  context.metadata[context.name] = { type: "computed" };
617
612
  return value;
618
613
  }
619
- return computed$1(value);
614
+ return internalCreateComputed(value).c;
620
615
  }
621
616
  function source(obj) {
622
617
  return getSource(obj);
@@ -682,17 +677,7 @@ class CollectionAdministration extends Administration {
682
677
  return collectionMethods[name];
683
678
  }
684
679
  if (typeof val === "function") {
685
- return function() {
686
- if (process.env.NODE_ENV !== "production") {
687
- console.warn(
688
- `r-state-tree: Calling uninstrumented collection method "${String(
689
- name
690
- )}". Reactivity is not guaranteed for this call.`
691
- );
692
- }
693
- const result = val.apply(adm.source, arguments);
694
- return result === adm.source ? adm.proxy : result;
695
- };
680
+ return val;
696
681
  }
697
682
  return val;
698
683
  }
@@ -818,9 +803,6 @@ class CollectionAdministration extends Administration {
818
803
  const target = getSource(value);
819
804
  if (isObservable(value)) {
820
805
  this.trackExplicitObservable(target);
821
- } else {
822
- this.untrackExplicitObservable(target);
823
- this.untrackExplicitObservable(value);
824
806
  }
825
807
  if (!this.hasEntry(value)) {
826
808
  this.source.add(target);
@@ -1053,15 +1035,40 @@ function isArrayIndexKey(key) {
1053
1035
  return Number.isInteger(num) && num >= 0 && num < 4294967295 && // 2^32 - 1
1054
1036
  String(num) === keyStr;
1055
1037
  }
1038
+ function reportConflict() {
1039
+ if (process.env.NODE_ENV !== "production") {
1040
+ throw new Error(
1041
+ "r-state-tree: Cannot assign the same object as both observable and raw source in the same array. The value will be treated as observable."
1042
+ );
1043
+ }
1044
+ }
1056
1045
  class ArrayAdministration extends Administration {
1057
1046
  valuesMap;
1058
1047
  keysAtom;
1059
- explicitObservables = /* @__PURE__ */ new Set();
1060
- syncExplicitObservablesFromSource() {
1061
- for (let i = 0; i < this.source.length; i++) {
1062
- const value = this.source[i];
1048
+ /**
1049
+ * Parent-based ownership tracking: sources assigned as observable.
1050
+ * Unlike index-based tracking, this doesn't require updates on reorder ops.
1051
+ */
1052
+ observableSources = /* @__PURE__ */ new WeakSet();
1053
+ rawSources = /* @__PURE__ */ new WeakSet();
1054
+ /**
1055
+ * Track a value being assigned to this array.
1056
+ * Detects conflicts if same source is assigned as both observable and raw.
1057
+ */
1058
+ trackValue(value) {
1059
+ if (value && typeof value === "object") {
1060
+ const source2 = getSource(value);
1063
1061
  if (isObservable(value)) {
1064
- this.explicitObservables.add(i);
1062
+ if (this.rawSources.has(source2)) {
1063
+ reportConflict();
1064
+ }
1065
+ this.observableSources.add(source2);
1066
+ } else {
1067
+ if (this.observableSources.has(source2)) {
1068
+ reportConflict();
1069
+ } else {
1070
+ this.rawSources.add(source2);
1071
+ }
1065
1072
  }
1066
1073
  }
1067
1074
  }
@@ -1118,7 +1125,6 @@ class ArrayAdministration extends Administration {
1118
1125
  if (result && had && isIndex) {
1119
1126
  const index = Number(name);
1120
1127
  adm.onArrayChanged(true, index, 1);
1121
- adm.explicitObservables.delete(index);
1122
1128
  }
1123
1129
  return result;
1124
1130
  },
@@ -1144,16 +1150,12 @@ class ArrayAdministration extends Administration {
1144
1150
  static methods = {
1145
1151
  fill(value, start, end) {
1146
1152
  const adm = getAdministration(this);
1147
- const shouldTrack = isObservable(value);
1153
+ adm.trackValue(value);
1148
1154
  const targetValue = getSource(value);
1149
1155
  adm.source.fill(targetValue, start, end);
1150
1156
  const length = adm.source.length;
1151
1157
  const from = start == null ? 0 : start < 0 ? Math.max(length + start, 0) : start;
1152
1158
  const to = end == null ? length : end < 0 ? Math.max(length + end, 0) : Math.min(end, length);
1153
- for (let i = from; i < to; i++) {
1154
- if (shouldTrack) adm.explicitObservables.add(i);
1155
- else adm.explicitObservables.delete(i);
1156
- }
1157
1159
  if (from < to) {
1158
1160
  adm.onArrayChanged(false, from, to - from);
1159
1161
  }
@@ -1192,41 +1194,24 @@ class ArrayAdministration extends Administration {
1192
1194
  },
1193
1195
  reverse() {
1194
1196
  const adm = getAdministration(this);
1195
- adm.syncExplicitObservablesFromSource();
1196
- const flags = new Array(adm.source.length);
1197
- for (let i = 0; i < flags.length; i++) {
1198
- flags[i] = adm.explicitObservables.has(i);
1199
- }
1200
1197
  adm.source.reverse();
1201
- flags.reverse();
1202
- adm.explicitObservables.clear();
1203
- for (let i = 0; i < flags.length; i++) {
1204
- if (flags[i]) adm.explicitObservables.add(i);
1205
- }
1206
1198
  adm.onArrayChanged(false, 0, adm.source.length);
1207
1199
  return this;
1208
1200
  },
1209
1201
  sort(compareFn) {
1210
1202
  const adm = getAdministration(this);
1211
- adm.syncExplicitObservablesFromSource();
1212
- const pairs = new Array(adm.source.length);
1213
- for (let i = 0; i < adm.source.length; i++) {
1214
- pairs[i] = {
1215
- value: this[i],
1216
- stored: adm.source[i],
1217
- observed: adm.explicitObservables.has(i)
1218
- };
1219
- }
1203
+ const pairs = adm.source.map((stored, i) => ({
1204
+ value: this[i],
1205
+ stored
1206
+ }));
1220
1207
  const comparator = compareFn ?? ((a, b) => {
1221
1208
  const as = String(a);
1222
1209
  const bs = String(b);
1223
1210
  return as < bs ? -1 : as > bs ? 1 : 0;
1224
1211
  });
1225
1212
  pairs.sort((a, b) => comparator(a.value, b.value));
1226
- adm.explicitObservables.clear();
1227
1213
  for (let i = 0; i < pairs.length; i++) {
1228
1214
  adm.source[i] = pairs[i].stored;
1229
- if (pairs[i].observed) adm.explicitObservables.add(i);
1230
1215
  }
1231
1216
  adm.onArrayChanged(false, 0, adm.source.length);
1232
1217
  return this;
@@ -1242,17 +1227,7 @@ class ArrayAdministration extends Administration {
1242
1227
  flat: createCopyMethod("flat"),
1243
1228
  copyWithin(target, start, end) {
1244
1229
  const adm = getAdministration(this);
1245
- adm.syncExplicitObservablesFromSource();
1246
- const flags = new Array(adm.source.length);
1247
- for (let i = 0; i < flags.length; i++) {
1248
- flags[i] = adm.explicitObservables.has(i);
1249
- }
1250
1230
  adm.source.copyWithin(target, start, end);
1251
- flags.copyWithin(target, start, end);
1252
- adm.explicitObservables.clear();
1253
- for (let i = 0; i < flags.length; i++) {
1254
- if (flags[i]) adm.explicitObservables.add(i);
1255
- }
1256
1231
  adm.onArrayChanged(false, 0, adm.source.length);
1257
1232
  return this;
1258
1233
  },
@@ -1286,23 +1261,20 @@ class ArrayAdministration extends Administration {
1286
1261
  _getEffectiveValue(index) {
1287
1262
  const value = this.source[index];
1288
1263
  if (value && typeof value === "object" && !Object.isFrozen(value)) {
1289
- if (this.explicitObservables.has(index)) {
1290
- const existingAdm = getAdministration(value);
1291
- if (existingAdm) {
1292
- if (existingAdm.proxy !== value) {
1293
- const desc = Object.getOwnPropertyDescriptor(this.source, index);
1294
- if (desc && !desc.configurable && !desc.writable) {
1295
- if (process.env.NODE_ENV !== "production") {
1296
- console.warn(
1297
- `r-state-tree: cannot return an observable proxy for arr[${index}] because it is a non-configurable, non-writable data property; returning the raw value to uphold Proxy invariants.`
1298
- );
1299
- }
1300
- this.explicitObservables.delete(index);
1301
- return value;
1264
+ if (this.observableSources.has(value)) {
1265
+ const adm = getAdministration(value);
1266
+ if (adm.proxy !== value) {
1267
+ const desc = Object.getOwnPropertyDescriptor(this.source, index);
1268
+ if (desc && !desc.configurable && !desc.writable) {
1269
+ if (process.env.NODE_ENV !== "production") {
1270
+ console.warn(
1271
+ `r-state-tree: cannot return an observable proxy for arr[${index}] because it is a non-configurable, non-writable data property; returning the raw value to uphold Proxy invariants.`
1272
+ );
1302
1273
  }
1274
+ return value;
1303
1275
  }
1304
- return existingAdm.proxy;
1305
1276
  }
1277
+ return adm.proxy;
1306
1278
  }
1307
1279
  }
1308
1280
  return value;
@@ -1311,23 +1283,17 @@ class ArrayAdministration extends Administration {
1311
1283
  const values = this.source;
1312
1284
  const targetValue = getSource(newValue);
1313
1285
  const oldLength = values.length;
1286
+ this.trackValue(newValue);
1314
1287
  let changed = true;
1315
1288
  if (index < oldLength) {
1316
1289
  const oldValue = values[index];
1317
- const oldExplicit = this.explicitObservables.has(index);
1318
- const newExplicit = isObservable(newValue);
1319
- changed = !isObservable(oldValue) && oldExplicit !== newExplicit || (isObservable(oldValue) ? newValue !== oldValue : targetValue !== oldValue);
1290
+ changed = targetValue !== oldValue;
1320
1291
  }
1321
1292
  if (!changed) return true;
1322
1293
  const result = Reflect.set(values, index, targetValue);
1323
1294
  if (!result) return false;
1324
1295
  const newLength = values.length;
1325
1296
  const lengthChanged = newLength !== oldLength;
1326
- if (isObservable(newValue)) {
1327
- this.explicitObservables.add(index);
1328
- } else {
1329
- this.explicitObservables.delete(index);
1330
- }
1331
1297
  this.onArrayChanged(lengthChanged, index, 1);
1332
1298
  return true;
1333
1299
  }
@@ -1348,15 +1314,6 @@ class ArrayAdministration extends Administration {
1348
1314
  const result = Reflect.set(this.source, "length", newLength);
1349
1315
  if (!result) return false;
1350
1316
  if (newLength < currentLength) {
1351
- const toRemove = [];
1352
- for (const index of this.explicitObservables) {
1353
- if (index >= newLength) {
1354
- toRemove.push(index);
1355
- }
1356
- }
1357
- for (const index of toRemove) {
1358
- this.explicitObservables.delete(index);
1359
- }
1360
1317
  this.onArrayChanged(true, newLength, currentLength - newLength);
1361
1318
  } else {
1362
1319
  this.onArrayChanged(true, currentLength, newLength - currentLength);
@@ -1366,13 +1323,10 @@ class ArrayAdministration extends Administration {
1366
1323
  spliceWithArray(index, deleteCount, newItems) {
1367
1324
  const length = this.source.length;
1368
1325
  const newTargetItems = [];
1369
- const newObservableIndices = [];
1370
1326
  if (newItems) {
1371
1327
  for (let i = 0; i < newItems.length; i++) {
1372
1328
  newTargetItems[i] = getSource(newItems[i]);
1373
- if (isObservable(newItems[i])) {
1374
- newObservableIndices.push(i);
1375
- }
1329
+ this.trackValue(newItems[i]);
1376
1330
  }
1377
1331
  }
1378
1332
  if (index === void 0) index = 0;
@@ -1385,31 +1339,10 @@ class ArrayAdministration extends Administration {
1385
1339
  for (let i = index; i < index + deleteCount; i++) {
1386
1340
  removedItems.push(this._getEffectiveValue(i));
1387
1341
  }
1388
- for (let i = index; i < index + deleteCount; i++) {
1389
- this.explicitObservables.delete(i);
1390
- }
1391
- const shift = (newItems?.length ?? 0) - deleteCount;
1392
- if (shift !== 0) {
1393
- const newExplicitObservables = /* @__PURE__ */ new Set();
1394
- for (const idx of this.explicitObservables) {
1395
- if (idx < index) {
1396
- newExplicitObservables.add(idx);
1397
- } else if (idx >= index + deleteCount) {
1398
- newExplicitObservables.add(idx + shift);
1399
- }
1400
- }
1401
- this.explicitObservables.clear();
1402
- for (const idx of newExplicitObservables) {
1403
- this.explicitObservables.add(idx);
1404
- }
1405
- }
1406
- for (const relativeIdx of newObservableIndices) {
1407
- this.explicitObservables.add(index + relativeIdx);
1408
- }
1409
1342
  this.spliceItemsIntoValues(index, deleteCount, newTargetItems);
1410
1343
  if (deleteCount !== 0 || newTargetItems.length !== 0) {
1411
- const shift2 = (newItems?.length ?? 0) - deleteCount;
1412
- const reindexing = shift2 !== 0 && index + deleteCount < length;
1344
+ const shift = (newItems?.length ?? 0) - deleteCount;
1345
+ const reindexing = shift !== 0 && index + deleteCount < length;
1413
1346
  const count = reindexing ? Number.POSITIVE_INFINITY : Math.max(deleteCount ?? 0, newItems?.length ?? 0);
1414
1347
  this.onArrayChanged(length !== this.source.length, index, count);
1415
1348
  }
@@ -2436,19 +2369,7 @@ function getIdKey(Ctor) {
2436
2369
  function getModelRefSnapshot(modelRef2) {
2437
2370
  const Ctor = Object.getPrototypeOf(modelRef2).constructor;
2438
2371
  const idKey = getIdKey(Ctor);
2439
- if (!idKey) return null;
2440
- let id2 = getIdentifier(modelRef2);
2441
- if (id2 === void 0) {
2442
- const source2 = getSource(modelRef2);
2443
- id2 = getIdentifier(source2);
2444
- if (id2 === void 0) {
2445
- const adm = getAdministration(modelRef2);
2446
- if (adm && adm.proxy !== modelRef2) {
2447
- id2 = getIdentifier(adm.proxy);
2448
- }
2449
- }
2450
- }
2451
- return { [idKey]: id2 };
2372
+ return idKey ? { [idKey]: getIdentifier(modelRef2) } : null;
2452
2373
  }
2453
2374
  function getSnapshotId(snapshot, Ctor) {
2454
2375
  const idKey = getIdKey(Ctor);
@@ -2572,60 +2493,9 @@ class ModelAdministration extends PreactObjectAdministration {
2572
2493
  modelsTraceUnsub = /* @__PURE__ */ new Map();
2573
2494
  writeInProgress = /* @__PURE__ */ new Set();
2574
2495
  computedSnapshot;
2575
- snapshotAtom = createAtom();
2576
2496
  snapshotMap = /* @__PURE__ */ new Map();
2577
2497
  contextCache = /* @__PURE__ */ new Map();
2578
2498
  parentName = null;
2579
- /**
2580
- * `@state` fields are shallow-reactive (property-level assignment triggers reactivity)
2581
- * and participate in snapshots. Values stored in `@state` are not deep-wrapped.
2582
- *
2583
- * This helper walks a value and:
2584
- * - observes any observable containers found (proxy or source) so in-place mutations
2585
- * invalidate `getSnapshot()` / `toSnapshot()`
2586
- * - avoids proxy traversal by preferring `getSource(...)`
2587
- * - guards against cycles via `WeakSet`
2588
- */
2589
- observeSnapshotValue(value, seen) {
2590
- if (value == null) return;
2591
- if (typeof value !== "object") return;
2592
- if (!seen) seen = /* @__PURE__ */ new WeakSet();
2593
- const obj = value;
2594
- if (seen.has(obj)) return;
2595
- seen.add(obj);
2596
- const adm = getAdministration(obj);
2597
- if (adm) {
2598
- adm.reportObserved();
2599
- }
2600
- const src = getSource(value);
2601
- if (src && typeof src === "object" && src !== value) {
2602
- this.observeSnapshotValue(src, seen);
2603
- return;
2604
- }
2605
- if (Array.isArray(value)) {
2606
- for (let i = 0; i < value.length; i++) {
2607
- this.observeSnapshotValue(value[i], seen);
2608
- }
2609
- return;
2610
- }
2611
- if (value instanceof Map) {
2612
- value.forEach((v, k) => {
2613
- this.observeSnapshotValue(k, seen);
2614
- this.observeSnapshotValue(v, seen);
2615
- });
2616
- return;
2617
- }
2618
- if (value instanceof Set) {
2619
- value.forEach((v) => this.observeSnapshotValue(v, seen));
2620
- return;
2621
- }
2622
- if (value instanceof Date) {
2623
- return;
2624
- }
2625
- for (const key of Object.keys(value)) {
2626
- this.observeSnapshotValue(value[key], seen);
2627
- }
2628
- }
2629
2499
  get parent() {
2630
2500
  this.parentAtom.reportObserved();
2631
2501
  return this._parent;
@@ -2661,7 +2531,6 @@ class ModelAdministration extends PreactObjectAdministration {
2661
2531
  value,
2662
2532
  this.proxy
2663
2533
  );
2664
- this.snapshotAtom.reportChanged();
2665
2534
  });
2666
2535
  }
2667
2536
  }
@@ -2857,9 +2726,7 @@ class ModelAdministration extends PreactObjectAdministration {
2857
2726
  return Object.keys(this.configuration).reduce((json, key) => {
2858
2727
  switch (this.configuration[key].type) {
2859
2728
  case ModelCfgTypes.state: {
2860
- const value = this.proxy[key];
2861
- this.observeSnapshotValue(value);
2862
- json[key] = clone(getSource(value), key);
2729
+ json[key] = clone(this.proxy[key], key);
2863
2730
  break;
2864
2731
  }
2865
2732
  case ModelCfgTypes.id:
@@ -3002,7 +2869,6 @@ class ModelAdministration extends PreactObjectAdministration {
3002
2869
  getSnapshot() {
3003
2870
  if (!this.computedSnapshot) {
3004
2871
  this.computedSnapshot = createComputed(() => {
3005
- this.snapshotAtom.reportObserved();
3006
2872
  const json = this.toJSON();
3007
2873
  configMap.set(json, this.configuration);
3008
2874
  return json;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "r-state-tree",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "reactive state management library",
5
5
  "main": "dist/r-state-tree.cjs",
6
6
  "module": "dist/r-state-tree.js",
@@ -1,2 +0,0 @@
1
- import type { ComputedNode } from "./observables";
2
- export default function <T extends Record<PropertyKey, any>>(computed: ComputedNode<T>): T;