@montra-interactive/deepstate 0.2.2 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/deepstate.d.ts.map +1 -1
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +10 -3
- package/dist/index.js +36 -6
- package/package.json +1 -1
- package/src/deepstate.ts +42 -3
- package/src/helpers.ts +15 -4
package/dist/deepstate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deepstate.d.ts","sourceRoot":"","sources":["../src/deepstate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAmB,UAAU,EAAqB,YAAY,EAAE,MAAM,MAAM,CAAC;AAapF,eAAO,IAAI,iBAAiB,QAAI,CAAC;AACjC,wBAAgB,sBAAsB,SAErC;
|
|
1
|
+
{"version":3,"file":"deepstate.d.ts","sourceRoot":"","sources":["../src/deepstate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAmB,UAAU,EAAqB,YAAY,EAAE,MAAM,MAAM,CAAC;AAapF,eAAO,IAAI,iBAAiB,QAAI,CAAC;AACjC,wBAAgB,sBAAsB,SAErC;AAiFD,KAAK,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;AAGhF,KAAK,eAAe,CAAC,CAAC,IAAI,CAAC,SAAS,IAAI,GAAG,SAAS,GAAG,KAAK,GAAG,CAAC,CAAC;AAGjE,KAAK,OAAO,CAAC,CAAC,IAAI,IAAI,SAAS,CAAC,GAAG,IAAI,GAAG,KAAK,CAAC;AAChD,KAAK,YAAY,CAAC,CAAC,IAAI,SAAS,SAAS,CAAC,GAAG,IAAI,GAAG,KAAK,CAAC;AAC1D,KAAK,SAAS,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,SAAS,IAAI,GAAG,IAAI,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;AAGrE,KAAK,mBAAmB,CAAC,CAAC,IAAI,eAAe,CAAC,CAAC,CAAC,SAAS,MAAM,GAC3D,eAAe,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,OAAO,CAAC,GACvC,KAAK,GACL,IAAI,GACN,KAAK,CAAC;AAGV,KAAK,gBAAgB,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,SAAS,IAAI,GAChD,mBAAmB,CAAC,CAAC,CAAC,GACtB,KAAK,CAAC;AAEV;;;GAGG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,GACjD,CAAC,GACD,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,GAC1B,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,GAC9B,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,GAClB;IAAE,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,GAC/C,CAAC,CAAC;AAEV;;GAEG;AACH,MAAM,MAAM,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC;AAGzB,UAAU,QAAQ,CAAC,CAAC;IAClB,QAAQ,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IAC1B,GAAG,IAAI,CAAC,CAAC;IACT,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IACpB,aAAa,CAAC,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GAAG,YAAY,CAAC;CAC5D;AAGD,QAAA,MAAM,IAAI,eAAiB,CAAC;AAG5B,KAAK,MAAM,CAAC,CAAC,IAAI,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,GAAG;IAC7C,sCAAsC;IACtC,GAAG,IAAI,YAAY,CAAC,CAAC,CAAC,CAAC;IACvB,gBAAgB;IAChB,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IACpB,qEAAqE;IACrE,aAAa,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,YAAY,CAAC;IACxE,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;CACrB,CAAC;AAEF,KAAK,QAAQ,CAAC,CAAC,SAAS,MAAM,IAAI;KAC/B,CAAC,IAAI,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAChC,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,GAAG;IAChC,sCAAsC;IACtC,GAAG,IAAI,YAAY,CAAC,CAAC,CAAC,CAAC;IACvB,gBAAgB;IAChB,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IACpB;;;;;;;;;OASG;IACH,MAAM,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;IAChE,qEAAqE;IACrE,aAAa,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,YAAY,CAAC;IACxE,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;CACrB,CAAC;AAEF,KAAK,OAAO,CAAC,CAAC,IAAI,UAAU,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG;IAChD,sCAAsC;IACtC,GAAG,IAAI,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC;IACzB,gBAAgB;IAChB,GAAG,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC;IACtB;;;;;;;;;OASG;IACH,MAAM,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC;IACjE,qEAAqE;IACrE,aAAa,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,KAAK,IAAI,GAAG,YAAY,CAAC;IAC1E,mDAAmD;IACnD,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAC5C,2CAA2C;IAC3C,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,GAAG;QAAE,GAAG,IAAI,MAAM,CAAA;KAAE,CAAC;IAC/C,uCAAuC;IACvC,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,GAAG,MAAM,CAAC;IAC5B,oBAAoB;IACpB,GAAG,IAAI,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IACnC,0EAA0E;IAC1E,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;IAC7D,4BAA4B;IAC5B,MAAM,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,GAAG,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;IACjF,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC;CACvB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,KAAK,UAAU,CAAC,CAAC,EAAE,QAAQ,SAAS,MAAM,GAAG,eAAe,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,GAAG;IACxG,gDAAgD;IAChD,GAAG,IAAI,YAAY,CAAC,CAAC,CAAC,CAAC;IACvB,2DAA2D;IAC3D,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IACpB,qEAAqE;IACrE,aAAa,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,YAAY,CAAC;IACxE;;;;;;;OAOG;IACH,MAAM,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,QAAQ,CAAC,KAAK,IAAI,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;IACvE,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;CACrB,GAAG;KAMD,CAAC,IAAI,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;CACpD,CAAC;AAEF;;;;;GAKG;AACH,KAAK,eAAe,CAAC,CAAC,IAEpB,gBAAgB,CAAC,CAAC,CAAC,SAAS,IAAI,GAC5B,UAAU,CAAC,CAAC,CAAC,GAEf,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,GACrB,MAAM,CAAC,CAAC,GAAG,SAAS,CAAC,GAEvB,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,GAC1B,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,GAEhC,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,GAClB,qBAAqB,CAAC,CAAC,CAAC,GAE1B,MAAM,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;AAE1B;;;;GAIG;AACH,KAAK,qBAAqB,CAAC,CAAC,SAAS,MAAM,IAAI,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,GAAG;IACvF,GAAG,IAAI,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IACnC,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IACpB,aAAa,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS,KAAK,IAAI,GAAG,YAAY,CAAC;IACpF,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;CACjC,GAAG;KACD,CAAC,IAAI,MAAM,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CACtC,CAAC;AAEF,KAAK,SAAS,CAAC,CAAC,IAEd,gBAAgB,CAAC,CAAC,CAAC,SAAS,IAAI,GAC5B,UAAU,CAAC,CAAC,CAAC,GAEf,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,GACrB,MAAM,CAAC,CAAC,CAAC,GAEX,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,GAC1B,OAAO,CAAC,CAAC,CAAC,GAEZ,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,GAClB,QAAQ,CAAC,CAAC,CAAC,GAEb,MAAM,CAAC,CAAC,CAAC,CAAC;AAEd,MAAM,MAAM,OAAO,CAAC,CAAC,SAAS,MAAM,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC;AA4jCpD,MAAM,WAAW,YAAY;IAC3B,0CAA0C;IAC1C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,wDAAwD;IACxD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,KAAK,CAAC,CAAC,SAAS,MAAM,EAAE,YAAY,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,CAAC,CAAC,CAQ3F;AAMD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,IAAI,CAMpE;AAUD,kDAAkD;AAClD,MAAM,MAAM,aAAa,CAAC,CAAC,IACvB,KAAK,GACL,SAAS,GACT,MAAM,GACN,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,OAAO,CAAC,CAAC;AAElC,qCAAqC;AACrC,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;CAC7B;AAMD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,OAAO,GAAE,YAAY,CAAC,CAAC,CAAM,GAAG,CAAC,EAAE,CAIvE"}
|
package/dist/helpers.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAiB,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAiB,MAAM,MAAM,CAAC;AA+CjD,KAAK,eAAe,CAAC,CAAC,IAAI,CAAC,SAAS,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;AAGpE,KAAK,gBAAgB,CAAC,CAAC,SAAS,UAAU,CAAC,OAAO,CAAC,EAAE,IAAI;KACtD,CAAC,IAAI,MAAM,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CACtC,CAAC;AAGF,KAAK,sBAAsB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI;KAC1E,CAAC,IAAI,MAAM,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CACtC,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,UAAU,CAAC,OAAO,CAAC,EAAE,EACpD,GAAG,WAAW,EAAE,CAAC,GAChB,UAAU,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;AACnC,wBAAgB,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,EAClE,WAAW,EAAE,CAAC,GACb,UAAU,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAC;AAyBzC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,CAAC,EACjC,SAAS,EAAE,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,EACvC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,CAAC,GACtC,UAAU,CAAC,CAAC,EAAE,CAAC,CAOjB"}
|
package/dist/helpers.js
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
// src/helpers.ts
|
|
2
2
|
import { combineLatest } from "rxjs";
|
|
3
3
|
import { distinctUntilChanged, map } from "rxjs/operators";
|
|
4
|
-
function deepEqual(a, b) {
|
|
4
|
+
function deepEqual(a, b, seen = new WeakMap) {
|
|
5
5
|
if (a === b)
|
|
6
6
|
return true;
|
|
7
7
|
if (a === null || b === null)
|
|
8
8
|
return false;
|
|
9
9
|
if (typeof a !== "object" || typeof b !== "object")
|
|
10
10
|
return false;
|
|
11
|
+
const seenWithA = seen.get(a);
|
|
12
|
+
if (seenWithA?.has(b))
|
|
13
|
+
return true;
|
|
14
|
+
if (!seen.has(a)) {
|
|
15
|
+
seen.set(a, new WeakSet);
|
|
16
|
+
}
|
|
17
|
+
seen.get(a).add(b);
|
|
11
18
|
if (Array.isArray(a) && Array.isArray(b)) {
|
|
12
19
|
if (a.length !== b.length)
|
|
13
20
|
return false;
|
|
14
|
-
return a.every((item, i) => deepEqual(item, b[i]));
|
|
21
|
+
return a.every((item, i) => deepEqual(item, b[i], seen));
|
|
15
22
|
}
|
|
16
23
|
if (Array.isArray(a) !== Array.isArray(b))
|
|
17
24
|
return false;
|
|
@@ -19,7 +26,7 @@ function deepEqual(a, b) {
|
|
|
19
26
|
const keysB = Object.keys(b);
|
|
20
27
|
if (keysA.length !== keysB.length)
|
|
21
28
|
return false;
|
|
22
|
-
return keysA.every((key) => deepEqual(a[key], b[key]));
|
|
29
|
+
return keysA.every((key) => deepEqual(a[key], b[key], seen));
|
|
23
30
|
}
|
|
24
31
|
function isObservable(obj) {
|
|
25
32
|
return obj !== null && typeof obj === "object" && "subscribe" in obj && typeof obj.subscribe === "function";
|
package/dist/index.js
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
// src/helpers.ts
|
|
2
2
|
import { combineLatest } from "rxjs";
|
|
3
3
|
import { distinctUntilChanged, map } from "rxjs/operators";
|
|
4
|
-
function deepEqual(a, b) {
|
|
4
|
+
function deepEqual(a, b, seen = new WeakMap) {
|
|
5
5
|
if (a === b)
|
|
6
6
|
return true;
|
|
7
7
|
if (a === null || b === null)
|
|
8
8
|
return false;
|
|
9
9
|
if (typeof a !== "object" || typeof b !== "object")
|
|
10
10
|
return false;
|
|
11
|
+
const seenWithA = seen.get(a);
|
|
12
|
+
if (seenWithA?.has(b))
|
|
13
|
+
return true;
|
|
14
|
+
if (!seen.has(a)) {
|
|
15
|
+
seen.set(a, new WeakSet);
|
|
16
|
+
}
|
|
17
|
+
seen.get(a).add(b);
|
|
11
18
|
if (Array.isArray(a) && Array.isArray(b)) {
|
|
12
19
|
if (a.length !== b.length)
|
|
13
20
|
return false;
|
|
14
|
-
return a.every((item, i) => deepEqual(item, b[i]));
|
|
21
|
+
return a.every((item, i) => deepEqual(item, b[i], seen));
|
|
15
22
|
}
|
|
16
23
|
if (Array.isArray(a) !== Array.isArray(b))
|
|
17
24
|
return false;
|
|
@@ -19,7 +26,7 @@ function deepEqual(a, b) {
|
|
|
19
26
|
const keysB = Object.keys(b);
|
|
20
27
|
if (keysA.length !== keysB.length)
|
|
21
28
|
return false;
|
|
22
|
-
return keysA.every((key) => deepEqual(a[key], b[key]));
|
|
29
|
+
return keysA.every((key) => deepEqual(a[key], b[key], seen));
|
|
23
30
|
}
|
|
24
31
|
function isObservable(obj) {
|
|
25
32
|
return obj !== null && typeof obj === "object" && "subscribe" in obj && typeof obj.subscribe === "function";
|
|
@@ -84,21 +91,27 @@ function countedDistinctUntilChanged(compareFn) {
|
|
|
84
91
|
return a === b;
|
|
85
92
|
});
|
|
86
93
|
}
|
|
87
|
-
function
|
|
94
|
+
function deepFreezeImpl(obj, seen) {
|
|
88
95
|
if (obj === null || typeof obj !== "object")
|
|
89
96
|
return obj;
|
|
90
97
|
if (Object.isFrozen(obj))
|
|
91
98
|
return obj;
|
|
99
|
+
if (seen.has(obj))
|
|
100
|
+
return obj;
|
|
101
|
+
seen.add(obj);
|
|
92
102
|
Object.freeze(obj);
|
|
93
103
|
if (Array.isArray(obj)) {
|
|
94
|
-
obj.forEach((item) =>
|
|
104
|
+
obj.forEach((item) => deepFreezeImpl(item, seen));
|
|
95
105
|
} else {
|
|
96
106
|
Object.keys(obj).forEach((key) => {
|
|
97
|
-
|
|
107
|
+
deepFreezeImpl(obj[key], seen);
|
|
98
108
|
});
|
|
99
109
|
}
|
|
100
110
|
return obj;
|
|
101
111
|
}
|
|
112
|
+
function deepFreeze(obj) {
|
|
113
|
+
return deepFreezeImpl(obj, new WeakSet);
|
|
114
|
+
}
|
|
102
115
|
var NODE = Symbol("node");
|
|
103
116
|
function createLeafNode(value) {
|
|
104
117
|
const subject$ = new BehaviorSubject(value);
|
|
@@ -201,6 +214,9 @@ function createArrayNode(value, comparator) {
|
|
|
201
214
|
childCache,
|
|
202
215
|
get: () => deepFreeze([...subject$.getValue()]),
|
|
203
216
|
set: (v) => {
|
|
217
|
+
if (hasCircularReference(v)) {
|
|
218
|
+
throw new Error("Circular reference detected in array value. " + "Deepstate does not support circular references. " + "Please flatten your data structure or remove the circular reference.");
|
|
219
|
+
}
|
|
204
220
|
childCache.clear();
|
|
205
221
|
subject$.next([...v]);
|
|
206
222
|
},
|
|
@@ -548,6 +564,17 @@ function createNestedArrayProjection(parentArray$, index, key, initialValue) {
|
|
|
548
564
|
}
|
|
549
565
|
};
|
|
550
566
|
}
|
|
567
|
+
function hasCircularReference(obj, seen = new WeakSet) {
|
|
568
|
+
if (obj === null || typeof obj !== "object")
|
|
569
|
+
return false;
|
|
570
|
+
if (seen.has(obj))
|
|
571
|
+
return true;
|
|
572
|
+
seen.add(obj);
|
|
573
|
+
if (Array.isArray(obj)) {
|
|
574
|
+
return obj.some((item) => hasCircularReference(item, seen));
|
|
575
|
+
}
|
|
576
|
+
return Object.values(obj).some((value) => hasCircularReference(value, seen));
|
|
577
|
+
}
|
|
551
578
|
function createNodeForValue(value, maybeNullable = false) {
|
|
552
579
|
if (isNullableMarked(value)) {
|
|
553
580
|
delete value[NULLABLE_MARKER];
|
|
@@ -562,6 +589,9 @@ function createNodeForValue(value, maybeNullable = false) {
|
|
|
562
589
|
if (typeof value !== "object") {
|
|
563
590
|
return createLeafNode(value);
|
|
564
591
|
}
|
|
592
|
+
if (hasCircularReference(value)) {
|
|
593
|
+
throw new Error("Circular reference detected in state value. " + "Deepstate does not support circular references because each property becomes a reactive node. " + "Please flatten your data structure or remove the circular reference.");
|
|
594
|
+
}
|
|
565
595
|
if (Array.isArray(value)) {
|
|
566
596
|
if (isArrayMarked(value)) {
|
|
567
597
|
const options = value[ARRAY_MARKER];
|
package/package.json
CHANGED
package/src/deepstate.ts
CHANGED
|
@@ -79,23 +79,31 @@ function countedDistinctUntilChanged<T>(compareFn?: (a: T, b: T) => boolean) {
|
|
|
79
79
|
// Deep Freeze
|
|
80
80
|
// =============================================================================
|
|
81
81
|
|
|
82
|
-
function
|
|
82
|
+
function deepFreezeImpl<T>(obj: T, seen: WeakSet<object>): T {
|
|
83
83
|
if (obj === null || typeof obj !== "object") return obj;
|
|
84
84
|
if (Object.isFrozen(obj)) return obj;
|
|
85
85
|
|
|
86
|
+
// Circular reference protection
|
|
87
|
+
if (seen.has(obj as object)) return obj;
|
|
88
|
+
seen.add(obj as object);
|
|
89
|
+
|
|
86
90
|
Object.freeze(obj);
|
|
87
91
|
|
|
88
92
|
if (Array.isArray(obj)) {
|
|
89
|
-
obj.forEach((item) =>
|
|
93
|
+
obj.forEach((item) => deepFreezeImpl(item, seen));
|
|
90
94
|
} else {
|
|
91
95
|
Object.keys(obj).forEach((key) => {
|
|
92
|
-
|
|
96
|
+
deepFreezeImpl((obj as Record<string, unknown>)[key], seen);
|
|
93
97
|
});
|
|
94
98
|
}
|
|
95
99
|
|
|
96
100
|
return obj;
|
|
97
101
|
}
|
|
98
102
|
|
|
103
|
+
function deepFreeze<T>(obj: T): T {
|
|
104
|
+
return deepFreezeImpl(obj, new WeakSet<object>());
|
|
105
|
+
}
|
|
106
|
+
|
|
99
107
|
// =============================================================================
|
|
100
108
|
// Types
|
|
101
109
|
// =============================================================================
|
|
@@ -514,6 +522,14 @@ function createArrayNode<T>(
|
|
|
514
522
|
childCache,
|
|
515
523
|
get: () => deepFreeze([...subject$.getValue()]) as T[],
|
|
516
524
|
set: (v: T[]) => {
|
|
525
|
+
// Check for circular references in array items
|
|
526
|
+
if (hasCircularReference(v)) {
|
|
527
|
+
throw new Error(
|
|
528
|
+
'Circular reference detected in array value. ' +
|
|
529
|
+
'Deepstate does not support circular references. ' +
|
|
530
|
+
'Please flatten your data structure or remove the circular reference.'
|
|
531
|
+
);
|
|
532
|
+
}
|
|
517
533
|
// Clear child cache when array is replaced
|
|
518
534
|
childCache.clear();
|
|
519
535
|
subject$.next([...v]);
|
|
@@ -1067,6 +1083,19 @@ function createNestedArrayProjection<T>(
|
|
|
1067
1083
|
};
|
|
1068
1084
|
}
|
|
1069
1085
|
|
|
1086
|
+
// Helper to detect circular references in an object
|
|
1087
|
+
function hasCircularReference(obj: unknown, seen = new WeakSet<object>()): boolean {
|
|
1088
|
+
if (obj === null || typeof obj !== 'object') return false;
|
|
1089
|
+
if (seen.has(obj as object)) return true;
|
|
1090
|
+
seen.add(obj as object);
|
|
1091
|
+
|
|
1092
|
+
if (Array.isArray(obj)) {
|
|
1093
|
+
return obj.some(item => hasCircularReference(item, seen));
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
return Object.values(obj).some(value => hasCircularReference(value, seen));
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1070
1099
|
// Factory to create the right node type
|
|
1071
1100
|
// When maybeNullable is true and value is null/undefined, creates a NullableNodeCore
|
|
1072
1101
|
// that can later be upgraded to an object with children
|
|
@@ -1088,6 +1117,16 @@ function createNodeForValue<T>(value: T, maybeNullable: boolean = false): NodeCo
|
|
|
1088
1117
|
if (typeof value !== "object") {
|
|
1089
1118
|
return createLeafNode(value as Primitive) as NodeCore<T>;
|
|
1090
1119
|
}
|
|
1120
|
+
|
|
1121
|
+
// Check for circular references before creating nodes
|
|
1122
|
+
if (hasCircularReference(value)) {
|
|
1123
|
+
throw new Error(
|
|
1124
|
+
'Circular reference detected in state value. ' +
|
|
1125
|
+
'Deepstate does not support circular references because each property becomes a reactive node. ' +
|
|
1126
|
+
'Please flatten your data structure or remove the circular reference.'
|
|
1127
|
+
);
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1091
1130
|
if (Array.isArray(value)) {
|
|
1092
1131
|
// Check if array was marked with options via array() helper
|
|
1093
1132
|
if (isArrayMarked(value)) {
|
package/src/helpers.ts
CHANGED
|
@@ -8,15 +8,26 @@
|
|
|
8
8
|
import { Observable, combineLatest } from "rxjs";
|
|
9
9
|
import { distinctUntilChanged, map } from "rxjs/operators";
|
|
10
10
|
|
|
11
|
-
// Deep equality check
|
|
12
|
-
function deepEqual(a: unknown, b: unknown): boolean {
|
|
11
|
+
// Deep equality check with circular reference protection
|
|
12
|
+
function deepEqual(a: unknown, b: unknown, seen = new WeakMap<object, WeakSet<object>>()): boolean {
|
|
13
13
|
if (a === b) return true;
|
|
14
14
|
if (a === null || b === null) return false;
|
|
15
15
|
if (typeof a !== "object" || typeof b !== "object") return false;
|
|
16
16
|
|
|
17
|
+
// Circular reference protection: if we've already compared these two objects, return true
|
|
18
|
+
// (they're equal as far as we've seen, and going deeper would be infinite)
|
|
19
|
+
const seenWithA = seen.get(a as object);
|
|
20
|
+
if (seenWithA?.has(b as object)) return true;
|
|
21
|
+
|
|
22
|
+
// Track this comparison
|
|
23
|
+
if (!seen.has(a as object)) {
|
|
24
|
+
seen.set(a as object, new WeakSet());
|
|
25
|
+
}
|
|
26
|
+
seen.get(a as object)!.add(b as object);
|
|
27
|
+
|
|
17
28
|
if (Array.isArray(a) && Array.isArray(b)) {
|
|
18
29
|
if (a.length !== b.length) return false;
|
|
19
|
-
return a.every((item, i) => deepEqual(item, b[i]));
|
|
30
|
+
return a.every((item, i) => deepEqual(item, b[i], seen));
|
|
20
31
|
}
|
|
21
32
|
|
|
22
33
|
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
@@ -26,7 +37,7 @@ function deepEqual(a: unknown, b: unknown): boolean {
|
|
|
26
37
|
if (keysA.length !== keysB.length) return false;
|
|
27
38
|
|
|
28
39
|
return keysA.every((key) =>
|
|
29
|
-
deepEqual((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key])
|
|
40
|
+
deepEqual((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key], seen)
|
|
30
41
|
);
|
|
31
42
|
}
|
|
32
43
|
|