scope-state 0.1.5 → 0.1.7
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 +8 -1
- package/dist/core/proxy.d.ts +1 -0
- package/dist/core/proxy.d.ts.map +1 -1
- package/dist/core/snapshot.d.ts +9 -0
- package/dist/core/snapshot.d.ts.map +1 -0
- package/dist/core/tracking.d.ts +15 -3
- package/dist/core/tracking.d.ts.map +1 -1
- package/dist/hooks/useScope.d.ts +8 -7
- package/dist/hooks/useScope.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +172 -120
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +172 -120
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +17 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -170,7 +170,10 @@ const TodoList = () => {
|
|
|
170
170
|
### Core Functions
|
|
171
171
|
|
|
172
172
|
#### `useScope(selector)`
|
|
173
|
-
Subscribe to reactive state changes.
|
|
173
|
+
Subscribe to reactive state changes and receive a read-only snapshot for rendering.
|
|
174
|
+
|
|
175
|
+
Mutate state through the main `$` proxy. The value returned from `useScope()` is
|
|
176
|
+
intended for reads only.
|
|
174
177
|
|
|
175
178
|
```tsx
|
|
176
179
|
// Subscribe to entire object
|
|
@@ -184,6 +187,10 @@ const isAdmin = useScope(() => $.user.role === 'admin');
|
|
|
184
187
|
|
|
185
188
|
// Subscribe to array
|
|
186
189
|
const todos = useScope(() => $.todos);
|
|
190
|
+
|
|
191
|
+
// Mutate through the main store proxy
|
|
192
|
+
$.user.$merge({ name: 'John' });
|
|
193
|
+
$.todos.push({ id: 1, text: 'Ship feature', completed: false });
|
|
187
194
|
```
|
|
188
195
|
|
|
189
196
|
#### `configure(options)`
|
package/dist/core/proxy.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CustomMethods, CustomArrayMethods } from '../types';
|
|
2
2
|
export declare const proxyPathMap: WeakMap<object, string[]>;
|
|
3
|
+
export declare const proxyTargetMap: WeakMap<object, object>;
|
|
3
4
|
export declare const pathUsageStats: {
|
|
4
5
|
accessedPaths: Set<string>;
|
|
5
6
|
modifiedPaths: Set<string>;
|
package/dist/core/proxy.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/core/proxy.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/core/proxy.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAUlE,eAAO,MAAM,YAAY,2BAAkC,CAAC;AAC5D,eAAO,MAAM,cAAc,yBAAgC,CAAC;AAM5D,eAAO,MAAM,cAAc;;;;CAI1B,CAAC;AAGF,eAAO,MAAM,aAAa,aAAoB,CAAC;AAM/C,eAAO,MAAM,eAAe;;;;;CAK3B,CAAC;AAiGF,eAAO,MAAM,cAAc;;;;;;2BA4CF,MAAM;CAS9B,CAAC;AAEF;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,GAAG,GAAG,IAAI,CAErD;AAYD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,MAAM,EAClD,MAAM,EAAE,CAAC,EACT,IAAI,GAAE,MAAM,EAAO,EACnB,KAAK,GAAE,MAAU,GAChB,CAAC,GAAG,CAAC,CAAC,SAAS,GAAG,EAAE,GAAG,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC,CAwJrE;AAsTD;;GAEG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAUtC;AAED;;GAEG;AACH,wBAAgB,kBAAkB;;;;;;;EAMjC;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,UAAQ,GAAG,GAAG,CA4B3D"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ScopeSnapshot } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Create a read-only snapshot suitable for rendering.
|
|
4
|
+
*
|
|
5
|
+
* Snapshots are plain arrays/objects with no proxy methods, which makes them
|
|
6
|
+
* safe for React Compiler memoization while keeping `$` as the mutable API.
|
|
7
|
+
*/
|
|
8
|
+
export declare function createReadonlySnapshot<T>(value: T, seen?: WeakMap<object, unknown>): ScopeSnapshot<T>;
|
|
9
|
+
//# sourceMappingURL=snapshot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../../src/core/snapshot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAgB9C;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EACtC,KAAK,EAAE,CAAC,EACR,IAAI,2BAAiC,GACpC,aAAa,CAAC,CAAC,CAAC,CAsClB"}
|
package/dist/core/tracking.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Track dependencies during selector execution - tracks
|
|
2
|
+
* Track dependencies during selector execution - tracks full dotted paths
|
|
3
3
|
*/
|
|
4
4
|
export declare function trackDependencies<T>(selector: () => T): {
|
|
5
5
|
value: T;
|
|
@@ -13,16 +13,28 @@ export declare function isCurrentlyTracking(): boolean;
|
|
|
13
13
|
* Temporarily skip tracking (for array method internals)
|
|
14
14
|
*/
|
|
15
15
|
export declare function skipTracking<T>(fn: () => T): T;
|
|
16
|
+
/**
|
|
17
|
+
* Run fn while capturing any newly tracked paths, then remove them from the main set.
|
|
18
|
+
* Caller decides whether to re-add captured paths (e.g. on a matched find iteration).
|
|
19
|
+
*/
|
|
20
|
+
export declare function capturePathsDuring<T>(fn: () => T): {
|
|
21
|
+
value: T;
|
|
22
|
+
paths: string[];
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Add paths to the active tracking set (used by smart array method overrides)
|
|
26
|
+
*/
|
|
27
|
+
export declare function addTrackedPaths(paths: string[]): void;
|
|
16
28
|
/**
|
|
17
29
|
* Get current tracking state (for debugging)
|
|
18
30
|
*/
|
|
19
31
|
export declare function getTrackingState(): {
|
|
20
32
|
isTracking: boolean;
|
|
21
|
-
|
|
33
|
+
trackedPaths: string[];
|
|
22
34
|
skipDepth: number;
|
|
23
35
|
};
|
|
24
36
|
/**
|
|
25
|
-
* Add a path
|
|
37
|
+
* Add a full path to tracking during proxy get operations
|
|
26
38
|
*/
|
|
27
39
|
export declare function trackPathAccess(path: string[]): void;
|
|
28
40
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracking.d.ts","sourceRoot":"","sources":["../../src/core/tracking.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG;IAAE,KAAK,EAAE,CAAC,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"tracking.d.ts","sourceRoot":"","sources":["../../src/core/tracking.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG;IAAE,KAAK,EAAE,CAAC,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAgBrF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,OAAO,CAE7C;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAO9C;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG;IAAE,KAAK,EAAE,CAAC,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAahF;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAGrD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI;IAClC,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;CACnB,CAMA;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CASpD;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAK1D;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,IAAI,CAIpC"}
|
package/dist/hooks/useScope.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ScopeSnapshot } from '../types';
|
|
2
2
|
/**
|
|
3
3
|
* Hook to subscribe to the global store and re-render when specific data changes.
|
|
4
4
|
*
|
|
@@ -6,15 +6,16 @@ import type { CustomMethods, CustomArrayMethods } from '../types';
|
|
|
6
6
|
* re-renders the component when those specific paths change. This provides
|
|
7
7
|
* fine-grained reactivity without unnecessary renders.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* The returned value is a read-only snapshot of the selected state. Mutate the
|
|
10
|
+
* store through the main `$` proxy (or a proxy created explicitly for commands),
|
|
11
|
+
* and use the hook return value only for rendering.
|
|
11
12
|
*
|
|
12
13
|
* @example
|
|
13
14
|
* // Subscribe to user data
|
|
14
15
|
* const user = useScope(() => $.user);
|
|
15
16
|
*
|
|
16
|
-
* //
|
|
17
|
-
* user.$merge({ name: 'New Name' });
|
|
17
|
+
* // Mutate through the main store proxy
|
|
18
|
+
* $.user.$merge({ name: 'New Name' });
|
|
18
19
|
*
|
|
19
20
|
* // Subscribe to a specific property
|
|
20
21
|
* const userName = useScope(() => $.user.name);
|
|
@@ -23,7 +24,7 @@ import type { CustomMethods, CustomArrayMethods } from '../types';
|
|
|
23
24
|
* const isAdmin = useScope(() => $.user.role === 'admin');
|
|
24
25
|
*
|
|
25
26
|
* @param selector - Function that returns the data you want to subscribe to
|
|
26
|
-
* @returns
|
|
27
|
+
* @returns A read-only snapshot of the selected data
|
|
27
28
|
*/
|
|
28
|
-
export declare function useScope<T>(selector: () => T):
|
|
29
|
+
export declare function useScope<T>(selector: () => T): ScopeSnapshot<T>;
|
|
29
30
|
//# sourceMappingURL=useScope.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useScope.d.ts","sourceRoot":"","sources":["../../src/hooks/useScope.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useScope.d.ts","sourceRoot":"","sources":["../../src/hooks/useScope.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EACxB,QAAQ,EAAE,MAAM,CAAC,GAChB,aAAa,CAAC,CAAC,CAAC,CA6ElB"}
|
package/dist/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export { persistenceAPI, hydrateState, mergePersistedIntoState } from './persist
|
|
|
7
7
|
export { createLocalStorageAdapter, createMemoryAdapter, createCachedAdapter, getDefaultAdapter } from './persistence/adapters';
|
|
8
8
|
export { setStorageAdapter, getStorageAdapter } from './persistence/storage';
|
|
9
9
|
export { setInitialStoreState, createAdvancedProxy, pathUsageStats, selectorPaths, clearProxyCache, getProxyCacheStats, optimizeMemoryUsage, proxyPathMap } from './core/proxy';
|
|
10
|
-
export type { ScopeConfig, ProxyConfig, MonitoringConfig, PersistenceConfig, StorageAdapter, MaybePromise, CustomMethods, CustomArrayMethods, StoreType, MonitoringStats, ProxyCacheStats, PathUsageStats } from './types';
|
|
10
|
+
export type { ScopeConfig, ProxyConfig, MonitoringConfig, PersistenceConfig, StorageAdapter, MaybePromise, CustomMethods, CustomArrayMethods, ScopeSnapshot, StoreType, MonitoringStats, ProxyCacheStats, PathUsageStats } from './types';
|
|
11
11
|
import type { StoreType, CustomMethods, CustomArrayMethods, ScopeConfig } from './types';
|
|
12
12
|
/**
|
|
13
13
|
* Configure Scope with custom settings and return a properly typed store
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAIrE,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAC/F,OAAO,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChI,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC7E,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,EACd,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EACnB,YAAY,EACb,MAAM,cAAc,CAAC;AAGtB,YAAY,EACV,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,YAAY,EACZ,aAAa,EACb,kBAAkB,EAClB,SAAS,EACT,eAAe,EACf,eAAe,EACf,cAAc,EACf,MAAM,SAAS,CAAC;AASjB,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAYzF;;;GAGG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACrD,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,GACrB,SAAS,CAAC,CAAC,CAAC,CAwEd;AAGD,wBAAgB,yBAAyB,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG;IAAE,KAAK,EAAE,CAAC,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAqB7F;AASD,eAAO,MAAM,CAAC,EAAE,SAAS,CAAC,GAAG,CAAoB,CAAC;AAGlD,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,SAAS,GAAG,EAAE,GAAG,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC,CAE5H;AAGD,eAAO,MAAM,MAAM,uBAAiB,CAAC;AAGrC,wBAAgB,UAAU,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAE5C;AAGD,wBAAgB,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAyBrC;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAEtC;AAED,wBAAgB,QAAQ,CAAC,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,GAAG,GAAG,CAAC,CAsClE;AAED,wBAAgB,IAAI,CAAC,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAK5D;AAYD,eAAO,MAAM,SAAS;;;;CAUrB,CAAC;AAGF,eAAO,MAAM,QAAQ,KAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAIrE,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAC/F,OAAO,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChI,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC7E,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,EACd,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EACnB,YAAY,EACb,MAAM,cAAc,CAAC;AAGtB,YAAY,EACV,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,YAAY,EACZ,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,SAAS,EACT,eAAe,EACf,eAAe,EACf,cAAc,EACf,MAAM,SAAS,CAAC;AASjB,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAYzF;;;GAGG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACrD,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,GACrB,SAAS,CAAC,CAAC,CAAC,CAwEd;AAGD,wBAAgB,yBAAyB,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG;IAAE,KAAK,EAAE,CAAC,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAqB7F;AASD,eAAO,MAAM,CAAC,EAAE,SAAS,CAAC,GAAG,CAAoB,CAAC;AAGlD,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,SAAS,GAAG,EAAE,GAAG,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC,CAE5H;AAGD,eAAO,MAAM,MAAM,uBAAiB,CAAC;AAGrC,wBAAgB,UAAU,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAE5C;AAGD,wBAAgB,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAyBrC;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAEtC;AAED,wBAAgB,QAAQ,CAAC,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,GAAG,GAAG,CAAC,CAsClE;AAED,wBAAgB,IAAI,CAAC,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAK5D;AAYD,eAAO,MAAM,SAAS;;;;CAUrB,CAAC;AAGF,eAAO,MAAM,QAAQ,KAAc,CAAC"}
|
package/dist/index.esm.js
CHANGED
|
@@ -351,6 +351,7 @@ function logSubscriptionRemoved(path) {
|
|
|
351
351
|
|
|
352
352
|
// Track proxy to path mapping for type-safe activation
|
|
353
353
|
const proxyPathMap = new WeakMap();
|
|
354
|
+
const proxyTargetMap = new WeakMap();
|
|
354
355
|
// Store a deep clone of the initial store state for use with $reset
|
|
355
356
|
let initialStoreState$1 = {};
|
|
356
357
|
// Path usage tracking
|
|
@@ -568,6 +569,11 @@ function createAdvancedProxy(target, path = [], depth = 0) {
|
|
|
568
569
|
if (typeof prop === 'symbol') {
|
|
569
570
|
return Reflect.get(obj, prop, receiver);
|
|
570
571
|
}
|
|
572
|
+
const value = Reflect.get(obj, prop, receiver);
|
|
573
|
+
// Functions are commands/helpers, not reactive data dependencies.
|
|
574
|
+
if (typeof value === 'function') {
|
|
575
|
+
return value;
|
|
576
|
+
}
|
|
571
577
|
const currentPropPath = [...path, prop.toString()];
|
|
572
578
|
const propPathKey = currentPropPath.join('.');
|
|
573
579
|
// Track path access during dependency tracking
|
|
@@ -578,9 +584,12 @@ function createAdvancedProxy(target, path = [], depth = 0) {
|
|
|
578
584
|
}
|
|
579
585
|
// Track path access for dependency tracking (inline to avoid circular dependency)
|
|
580
586
|
trackPathAccess(currentPropPath);
|
|
581
|
-
const value = obj[prop];
|
|
582
587
|
// For objects, create proxies for nested values
|
|
583
588
|
if (value && typeof value === 'object' && path.length < proxyConfig.maxDepth) {
|
|
589
|
+
// If value is already a proxy, return it directly to prevent double-wrapping
|
|
590
|
+
if (proxyPathMap.has(value)) {
|
|
591
|
+
return value;
|
|
592
|
+
}
|
|
584
593
|
const shouldProxy = !proxyConfig.lazyProxyDeepObjects ||
|
|
585
594
|
pathUsageStats.accessedPaths.has(propPathKey) ||
|
|
586
595
|
pathUsageStats.modifiedPaths.has(propPathKey) ||
|
|
@@ -592,27 +601,16 @@ function createAdvancedProxy(target, path = [], depth = 0) {
|
|
|
592
601
|
}
|
|
593
602
|
return value;
|
|
594
603
|
},
|
|
595
|
-
set(obj, prop, value
|
|
604
|
+
set(obj, prop, value) {
|
|
596
605
|
if (typeof prop === 'symbol') {
|
|
597
|
-
return Reflect.set(obj, prop, value
|
|
606
|
+
return Reflect.set(obj, prop, value);
|
|
598
607
|
}
|
|
599
608
|
const propPath = [...path, prop.toString()];
|
|
600
609
|
const propPathKey = propPath.join('.');
|
|
601
|
-
// Track modification
|
|
602
610
|
if (proxyConfig.trackPathUsage) {
|
|
603
611
|
pathUsageStats.modifiedPaths.add(propPathKey);
|
|
604
612
|
}
|
|
605
|
-
|
|
606
|
-
if (value && typeof value === 'object' && !proxyCache.has(value)) {
|
|
607
|
-
const newPath = [...path, prop.toString()];
|
|
608
|
-
const proxiedValue = createAdvancedProxy(value, newPath, 0);
|
|
609
|
-
const result = Reflect.set(obj, prop, proxiedValue, receiver);
|
|
610
|
-
// Notify the specific property path and all parent/child paths
|
|
611
|
-
notifyListeners(propPath);
|
|
612
|
-
return result;
|
|
613
|
-
}
|
|
614
|
-
const result = Reflect.set(obj, prop, value, receiver);
|
|
615
|
-
// Notify the specific property path (this will also notify parent/child paths)
|
|
613
|
+
const result = Reflect.set(obj, prop, value);
|
|
616
614
|
notifyListeners(propPath);
|
|
617
615
|
return result;
|
|
618
616
|
},
|
|
@@ -638,6 +636,7 @@ function createAdvancedProxy(target, path = [], depth = 0) {
|
|
|
638
636
|
proxyCacheLRU.add(target, proxy);
|
|
639
637
|
// Track the path for this proxy
|
|
640
638
|
proxyPathMap.set(proxy, [...path]);
|
|
639
|
+
proxyTargetMap.set(proxy, target);
|
|
641
640
|
return proxy;
|
|
642
641
|
}
|
|
643
642
|
/**
|
|
@@ -654,16 +653,7 @@ function addObjectMethods(target, path) {
|
|
|
654
653
|
const propPath = [...currentPath, key].join('.');
|
|
655
654
|
pathUsageStats.modifiedPaths.add(propPath);
|
|
656
655
|
}
|
|
657
|
-
|
|
658
|
-
if (newValue && typeof newValue === 'object' && !proxyCache.has(newValue)) {
|
|
659
|
-
const newPath = [...currentPath, key];
|
|
660
|
-
// Use Reflect.set with this proxy as the receiver to trigger the set handler
|
|
661
|
-
Reflect.set(this, key, createAdvancedProxy(newValue, newPath, 0), this);
|
|
662
|
-
}
|
|
663
|
-
else {
|
|
664
|
-
// Use Reflect.set with this proxy as the receiver to trigger the set handler
|
|
665
|
-
Reflect.set(this, key, newValue, this);
|
|
666
|
-
}
|
|
656
|
+
Reflect.set(this, key, newProps[key], this);
|
|
667
657
|
});
|
|
668
658
|
return this;
|
|
669
659
|
},
|
|
@@ -674,28 +664,17 @@ function addObjectMethods(target, path) {
|
|
|
674
664
|
methodsToDefine.$set = {
|
|
675
665
|
value: function (newProps) {
|
|
676
666
|
const currentPath = proxyPathMap.get(this) || path;
|
|
677
|
-
// Clear existing properties
|
|
678
667
|
Object.keys(this).forEach(key => {
|
|
679
668
|
if (typeof this[key] !== 'function') {
|
|
680
669
|
Reflect.deleteProperty(this, key);
|
|
681
670
|
}
|
|
682
671
|
});
|
|
683
|
-
// Set new properties
|
|
684
672
|
Object.keys(newProps || {}).forEach(key => {
|
|
685
673
|
if (proxyConfig.trackPathUsage) {
|
|
686
674
|
const propPath = [...currentPath, key].join('.');
|
|
687
675
|
pathUsageStats.modifiedPaths.add(propPath);
|
|
688
676
|
}
|
|
689
|
-
|
|
690
|
-
if (newValue && typeof newValue === 'object' && !proxyCache.has(newValue)) {
|
|
691
|
-
const newPath = [...currentPath, key];
|
|
692
|
-
// Use Reflect.set with this proxy as the receiver to trigger the set handler
|
|
693
|
-
Reflect.set(this, key, createAdvancedProxy(newValue, newPath, 0), this);
|
|
694
|
-
}
|
|
695
|
-
else {
|
|
696
|
-
// Use Reflect.set with this proxy as the receiver to trigger the set handler
|
|
697
|
-
Reflect.set(this, key, newValue, this);
|
|
698
|
-
}
|
|
677
|
+
Reflect.set(this, key, newProps[key], this);
|
|
699
678
|
});
|
|
700
679
|
return this;
|
|
701
680
|
},
|
|
@@ -725,12 +704,7 @@ function addObjectMethods(target, path) {
|
|
|
725
704
|
pathUsageStats.modifiedPaths.add(propPath);
|
|
726
705
|
}
|
|
727
706
|
const currentValue = this[key];
|
|
728
|
-
|
|
729
|
-
if (newValue && typeof newValue === 'object' && !proxyCache.has(newValue)) {
|
|
730
|
-
const newPath = [...currentPath, key];
|
|
731
|
-
newValue = createAdvancedProxy(newValue, newPath, 0);
|
|
732
|
-
}
|
|
733
|
-
// Use Reflect.set with this proxy as the receiver to trigger the set handler
|
|
707
|
+
const newValue = updater(currentValue);
|
|
734
708
|
Reflect.set(this, key, newValue, this);
|
|
735
709
|
return this;
|
|
736
710
|
},
|
|
@@ -788,20 +762,13 @@ function addObjectMethods(target, path) {
|
|
|
788
762
|
* Add custom methods to array targets
|
|
789
763
|
*/
|
|
790
764
|
function addArrayMethods(target, path) {
|
|
791
|
-
const originalPush =
|
|
792
|
-
const originalSplice =
|
|
765
|
+
const originalPush = Array.prototype.push;
|
|
766
|
+
const originalSplice = Array.prototype.splice;
|
|
793
767
|
// Override push
|
|
794
768
|
Object.defineProperty(target, 'push', {
|
|
795
769
|
value: function (...items) {
|
|
796
770
|
const currentPath = proxyPathMap.get(this) || path;
|
|
797
|
-
const
|
|
798
|
-
if (item && typeof item === 'object' && !proxyCache.has(item)) {
|
|
799
|
-
const itemPath = [...currentPath, '_item'];
|
|
800
|
-
return createAdvancedProxy(item, itemPath, 0);
|
|
801
|
-
}
|
|
802
|
-
return item;
|
|
803
|
-
});
|
|
804
|
-
const result = originalPush.apply(this, processedItems);
|
|
771
|
+
const result = originalPush.apply(this, items);
|
|
805
772
|
if (currentPath.length > 0) {
|
|
806
773
|
if (proxyConfig.trackPathUsage) {
|
|
807
774
|
pathUsageStats.modifiedPaths.add(currentPath.join('.'));
|
|
@@ -813,27 +780,40 @@ function addArrayMethods(target, path) {
|
|
|
813
780
|
writable: true,
|
|
814
781
|
configurable: true
|
|
815
782
|
});
|
|
783
|
+
// Override find with smart tracking during dependency collection
|
|
784
|
+
Object.defineProperty(target, 'find', {
|
|
785
|
+
value: function (predicate, thisArg) {
|
|
786
|
+
if (!isCurrentlyTracking() || !proxyConfig.smartArrayTracking) {
|
|
787
|
+
return Array.prototype.find.call(this, predicate, thisArg);
|
|
788
|
+
}
|
|
789
|
+
const currentPath = proxyPathMap.get(this) || path;
|
|
790
|
+
const length = skipTracking(() => this.length);
|
|
791
|
+
for (let i = 0; i < length; i++) {
|
|
792
|
+
const element = skipTracking(() => Reflect.get(this, i));
|
|
793
|
+
const { value: matched, paths } = capturePathsDuring(() => Boolean(predicate.call(thisArg, element, i, this)));
|
|
794
|
+
if (matched) {
|
|
795
|
+
addTrackedPaths([[...currentPath, String(i)].join('.')]);
|
|
796
|
+
addTrackedPaths(paths);
|
|
797
|
+
return element;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
return undefined;
|
|
801
|
+
},
|
|
802
|
+
writable: true,
|
|
803
|
+
configurable: true
|
|
804
|
+
});
|
|
816
805
|
// Override splice
|
|
817
806
|
Object.defineProperty(target, 'splice', {
|
|
818
807
|
value: function (start, deleteCount, ...items) {
|
|
819
808
|
const currentPath = proxyPathMap.get(this) || path;
|
|
820
809
|
const arrayLength = this.length;
|
|
821
|
-
const processedItems = items.map((item, index) => {
|
|
822
|
-
if (item && typeof item === 'object' && !proxyCache.has(item)) {
|
|
823
|
-
const itemPath = [...currentPath, (start + index).toString()];
|
|
824
|
-
return createAdvancedProxy(item, itemPath, 0);
|
|
825
|
-
}
|
|
826
|
-
return item;
|
|
827
|
-
});
|
|
828
810
|
const actualDeleteCount = deleteCount === undefined ? (arrayLength - start) : deleteCount;
|
|
829
|
-
const result = originalSplice.apply(this, [start, actualDeleteCount, ...
|
|
811
|
+
const result = originalSplice.apply(this, [start, actualDeleteCount, ...items]);
|
|
830
812
|
if (currentPath.length > 0) {
|
|
831
813
|
if (proxyConfig.trackPathUsage) {
|
|
832
814
|
pathUsageStats.modifiedPaths.add(currentPath.join('.'));
|
|
833
815
|
}
|
|
834
|
-
// Notify the array itself
|
|
835
816
|
notifyListeners(currentPath);
|
|
836
|
-
// Also notify about each index that was affected
|
|
837
817
|
for (let i = start; i < arrayLength; i++) {
|
|
838
818
|
const indexPath = [...currentPath, i.toString()];
|
|
839
819
|
notifyListeners(indexPath);
|
|
@@ -854,14 +834,7 @@ function addArrayMethods(target, path) {
|
|
|
854
834
|
}
|
|
855
835
|
const currentPath = proxyPathMap.get(this) || path;
|
|
856
836
|
this.length = 0;
|
|
857
|
-
|
|
858
|
-
if (item && typeof item === 'object' && !proxyCache.has(item)) {
|
|
859
|
-
const itemPath = [...currentPath, index.toString()];
|
|
860
|
-
return createAdvancedProxy(item, itemPath, 0);
|
|
861
|
-
}
|
|
862
|
-
return item;
|
|
863
|
-
});
|
|
864
|
-
originalPush.apply(this, processedItems);
|
|
837
|
+
originalPush.apply(this, newArray);
|
|
865
838
|
if (currentPath.length > 0) {
|
|
866
839
|
if (proxyConfig.trackPathUsage) {
|
|
867
840
|
pathUsageStats.modifiedPaths.add(currentPath.join('.'));
|
|
@@ -893,15 +866,8 @@ function addArrayMethods(target, path) {
|
|
|
893
866
|
initialValue = [];
|
|
894
867
|
}
|
|
895
868
|
this.length = 0;
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
const itemPath = [...currentPath, index.toString()];
|
|
899
|
-
return createAdvancedProxy(item, itemPath, 0);
|
|
900
|
-
}
|
|
901
|
-
return item;
|
|
902
|
-
});
|
|
903
|
-
if (processedItems.length > 0) {
|
|
904
|
-
originalPush.apply(this, processedItems);
|
|
869
|
+
if (initialValue.length > 0) {
|
|
870
|
+
originalPush.apply(this, JSON.parse(JSON.stringify(initialValue)));
|
|
905
871
|
}
|
|
906
872
|
if (currentPath.length > 0) {
|
|
907
873
|
notifyListeners(currentPath);
|
|
@@ -976,48 +942,125 @@ function optimizeMemoryUsage(aggressive = false) {
|
|
|
976
942
|
};
|
|
977
943
|
}
|
|
978
944
|
|
|
979
|
-
//
|
|
980
|
-
let
|
|
945
|
+
// Unique full dotted paths accessed during selector execution
|
|
946
|
+
let trackedPaths = new Set();
|
|
981
947
|
let isTracking = false;
|
|
982
948
|
let skipTrackingDepth = 0;
|
|
983
949
|
/**
|
|
984
|
-
* Track dependencies during selector execution - tracks
|
|
950
|
+
* Track dependencies during selector execution - tracks full dotted paths
|
|
985
951
|
*/
|
|
986
952
|
function trackDependencies(selector) {
|
|
987
|
-
// Start tracking
|
|
988
953
|
isTracking = true;
|
|
989
|
-
|
|
954
|
+
trackedPaths = new Set();
|
|
990
955
|
skipTrackingDepth = 0;
|
|
991
|
-
// Execute selector to track dependencies
|
|
992
956
|
const value = selector();
|
|
993
|
-
// Stop tracking and get the tracked paths
|
|
994
957
|
isTracking = false;
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
958
|
+
const paths = Array.from(trackedPaths).filter(path => path && path.length < 500);
|
|
959
|
+
trackedPaths = new Set();
|
|
960
|
+
return { value, paths };
|
|
961
|
+
}
|
|
962
|
+
/**
|
|
963
|
+
* Check if we're currently tracking dependencies
|
|
964
|
+
*/
|
|
965
|
+
function isCurrentlyTracking() {
|
|
966
|
+
return isTracking && skipTrackingDepth === 0;
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* Temporarily skip tracking (for array method internals)
|
|
970
|
+
*/
|
|
971
|
+
function skipTracking(fn) {
|
|
972
|
+
skipTrackingDepth++;
|
|
973
|
+
try {
|
|
974
|
+
return fn();
|
|
975
|
+
}
|
|
976
|
+
finally {
|
|
977
|
+
skipTrackingDepth--;
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* Run fn while capturing any newly tracked paths, then remove them from the main set.
|
|
982
|
+
* Caller decides whether to re-add captured paths (e.g. on a matched find iteration).
|
|
983
|
+
*/
|
|
984
|
+
function capturePathsDuring(fn) {
|
|
985
|
+
const snapshot = new Set(trackedPaths);
|
|
986
|
+
const value = fn();
|
|
987
|
+
const newPaths = [];
|
|
988
|
+
trackedPaths.forEach(path => {
|
|
989
|
+
if (!snapshot.has(path)) {
|
|
990
|
+
newPaths.push(path);
|
|
991
|
+
trackedPaths.delete(path);
|
|
992
|
+
}
|
|
999
993
|
});
|
|
1000
|
-
|
|
1001
|
-
|
|
994
|
+
return { value, paths: newPaths };
|
|
995
|
+
}
|
|
996
|
+
/**
|
|
997
|
+
* Add paths to the active tracking set (used by smart array method overrides)
|
|
998
|
+
*/
|
|
999
|
+
function addTrackedPaths(paths) {
|
|
1000
|
+
if (!isTracking)
|
|
1001
|
+
return;
|
|
1002
|
+
paths.forEach(path => trackedPaths.add(path));
|
|
1002
1003
|
}
|
|
1003
1004
|
/**
|
|
1004
|
-
* Add a path
|
|
1005
|
+
* Add a full path to tracking during proxy get operations
|
|
1005
1006
|
*/
|
|
1006
1007
|
function trackPathAccess(path) {
|
|
1007
1008
|
if (!isTracking || skipTrackingDepth > 0)
|
|
1008
1009
|
return;
|
|
1009
|
-
|
|
1010
|
-
const prop = path[path.length - 1];
|
|
1011
|
-
// Only track if prop exists and path isn't too deep
|
|
1012
|
-
if (prop && path.length <= proxyConfig.maxPathLength) {
|
|
1013
|
-
currentPath.push(prop);
|
|
1014
|
-
// Add full path to usage stats and selector paths for ultra-selective proxying
|
|
1010
|
+
if (path.length <= proxyConfig.maxPathLength) {
|
|
1015
1011
|
const fullPath = path.join('.');
|
|
1012
|
+
trackedPaths.add(fullPath);
|
|
1016
1013
|
pathUsageStats.accessedPaths.add(fullPath);
|
|
1017
1014
|
selectorPaths.add(fullPath);
|
|
1018
1015
|
}
|
|
1019
1016
|
}
|
|
1020
1017
|
|
|
1018
|
+
function isPlainObject(value) {
|
|
1019
|
+
const prototype = Object.getPrototypeOf(value);
|
|
1020
|
+
return prototype === Object.prototype || prototype === null;
|
|
1021
|
+
}
|
|
1022
|
+
function unwrapProxyTarget(value) {
|
|
1023
|
+
if (value && typeof value === 'object' && proxyTargetMap.has(value)) {
|
|
1024
|
+
return proxyTargetMap.get(value);
|
|
1025
|
+
}
|
|
1026
|
+
return value;
|
|
1027
|
+
}
|
|
1028
|
+
/**
|
|
1029
|
+
* Create a read-only snapshot suitable for rendering.
|
|
1030
|
+
*
|
|
1031
|
+
* Snapshots are plain arrays/objects with no proxy methods, which makes them
|
|
1032
|
+
* safe for React Compiler memoization while keeping `$` as the mutable API.
|
|
1033
|
+
*/
|
|
1034
|
+
function createReadonlySnapshot(value, seen = new WeakMap()) {
|
|
1035
|
+
if (value === null || typeof value !== 'object') {
|
|
1036
|
+
return value;
|
|
1037
|
+
}
|
|
1038
|
+
const source = unwrapProxyTarget(value);
|
|
1039
|
+
if (seen.has(source)) {
|
|
1040
|
+
return seen.get(source);
|
|
1041
|
+
}
|
|
1042
|
+
if (Array.isArray(source)) {
|
|
1043
|
+
const snapshot = [];
|
|
1044
|
+
seen.set(source, snapshot);
|
|
1045
|
+
source.forEach(item => {
|
|
1046
|
+
snapshot.push(createReadonlySnapshot(item, seen));
|
|
1047
|
+
});
|
|
1048
|
+
return snapshot;
|
|
1049
|
+
}
|
|
1050
|
+
if (!isPlainObject(source)) {
|
|
1051
|
+
return source;
|
|
1052
|
+
}
|
|
1053
|
+
const snapshot = {};
|
|
1054
|
+
seen.set(source, snapshot);
|
|
1055
|
+
Object.keys(source).forEach(key => {
|
|
1056
|
+
const propertyValue = source[key];
|
|
1057
|
+
if (typeof propertyValue !== 'function') {
|
|
1058
|
+
snapshot[key] = createReadonlySnapshot(propertyValue, seen);
|
|
1059
|
+
}
|
|
1060
|
+
});
|
|
1061
|
+
return snapshot;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1021
1064
|
/**
|
|
1022
1065
|
* Hook to subscribe to the global store and re-render when specific data changes.
|
|
1023
1066
|
*
|
|
@@ -1025,15 +1068,16 @@ function trackPathAccess(path) {
|
|
|
1025
1068
|
* re-renders the component when those specific paths change. This provides
|
|
1026
1069
|
* fine-grained reactivity without unnecessary renders.
|
|
1027
1070
|
*
|
|
1028
|
-
*
|
|
1029
|
-
*
|
|
1071
|
+
* The returned value is a read-only snapshot of the selected state. Mutate the
|
|
1072
|
+
* store through the main `$` proxy (or a proxy created explicitly for commands),
|
|
1073
|
+
* and use the hook return value only for rendering.
|
|
1030
1074
|
*
|
|
1031
1075
|
* @example
|
|
1032
1076
|
* // Subscribe to user data
|
|
1033
1077
|
* const user = useScope(() => $.user);
|
|
1034
1078
|
*
|
|
1035
|
-
* //
|
|
1036
|
-
* user.$merge({ name: 'New Name' });
|
|
1079
|
+
* // Mutate through the main store proxy
|
|
1080
|
+
* $.user.$merge({ name: 'New Name' });
|
|
1037
1081
|
*
|
|
1038
1082
|
* // Subscribe to a specific property
|
|
1039
1083
|
* const userName = useScope(() => $.user.name);
|
|
@@ -1042,22 +1086,24 @@ function trackPathAccess(path) {
|
|
|
1042
1086
|
* const isAdmin = useScope(() => $.user.role === 'admin');
|
|
1043
1087
|
*
|
|
1044
1088
|
* @param selector - Function that returns the data you want to subscribe to
|
|
1045
|
-
* @returns
|
|
1089
|
+
* @returns A read-only snapshot of the selected data
|
|
1046
1090
|
*/
|
|
1047
1091
|
function useScope(selector) {
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1092
|
+
const snapshotCacheRef = useRef({
|
|
1093
|
+
revision: -1,
|
|
1094
|
+
source: undefined,
|
|
1095
|
+
snapshot: undefined,
|
|
1096
|
+
});
|
|
1097
|
+
// Track dependencies and get the selected value from the store
|
|
1098
|
+
const { value: selectedValue, paths: trackedPaths } = trackDependencies(selector);
|
|
1053
1099
|
// Add tracked paths to selector paths for ultra-selective proxying
|
|
1054
1100
|
trackedPaths.forEach(path => {
|
|
1055
1101
|
selectorPaths.add(path);
|
|
1056
1102
|
pathUsageStats.subscribedPaths.add(path);
|
|
1057
1103
|
});
|
|
1058
|
-
// Use a counter to
|
|
1059
|
-
//
|
|
1060
|
-
const [, forceUpdate] = useState(0);
|
|
1104
|
+
// Use a counter to invalidate the cached snapshot only when this hook
|
|
1105
|
+
// receives a relevant store notification.
|
|
1106
|
+
const [revision, forceUpdate] = useState(0);
|
|
1061
1107
|
// Create stable update handler that forces re-render
|
|
1062
1108
|
const handleChange = useCallback(() => {
|
|
1063
1109
|
try {
|
|
@@ -1078,11 +1124,7 @@ function useScope(selector) {
|
|
|
1078
1124
|
handleChange();
|
|
1079
1125
|
}, 16);
|
|
1080
1126
|
}
|
|
1081
|
-
|
|
1082
|
-
// If trackedPaths is ['user', 'name'], create subscriptions for ['user', 'user.name']
|
|
1083
|
-
const pathKeys = trackedPaths.length > 0
|
|
1084
|
-
? trackedPaths.map((_, index, array) => array.slice(0, index + 1).join('.'))
|
|
1085
|
-
: [''];
|
|
1127
|
+
const pathKeys = trackedPaths.length > 0 ? trackedPaths : [''];
|
|
1086
1128
|
// Subscribe to all relevant paths
|
|
1087
1129
|
const unsubscribeFunctions = pathKeys.map(pathKey => {
|
|
1088
1130
|
// Mark this path as subscribed for usage tracking
|
|
@@ -1094,8 +1136,18 @@ function useScope(selector) {
|
|
|
1094
1136
|
unsubscribeFunctions.forEach(unsubscribe => unsubscribe());
|
|
1095
1137
|
};
|
|
1096
1138
|
}, [trackedPaths.join(','), handleChange]); // Stable dependencies
|
|
1097
|
-
|
|
1098
|
-
|
|
1139
|
+
if (selectedValue === null || typeof selectedValue !== 'object') {
|
|
1140
|
+
return selectedValue;
|
|
1141
|
+
}
|
|
1142
|
+
if (snapshotCacheRef.current.revision !== revision ||
|
|
1143
|
+
snapshotCacheRef.current.source !== selectedValue) {
|
|
1144
|
+
snapshotCacheRef.current = {
|
|
1145
|
+
revision,
|
|
1146
|
+
source: selectedValue,
|
|
1147
|
+
snapshot: createReadonlySnapshot(selectedValue),
|
|
1148
|
+
};
|
|
1149
|
+
}
|
|
1150
|
+
return snapshotCacheRef.current.snapshot;
|
|
1099
1151
|
}
|
|
1100
1152
|
|
|
1101
1153
|
/**
|