synstate 2.0.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/core/base/create-child-observable.d.mts +65 -0
- package/dist/core/base/create-child-observable.d.mts.map +1 -0
- package/dist/core/base/create-child-observable.mjs +157 -0
- package/dist/core/base/create-child-observable.mjs.map +1 -0
- package/dist/core/base/create-observable-base.d.mts +82 -0
- package/dist/core/base/create-observable-base.d.mts.map +1 -0
- package/dist/core/base/create-observable-base.mjs +160 -0
- package/dist/core/base/create-observable-base.mjs.map +1 -0
- package/dist/core/base/create-root-observable.d.mts +37 -0
- package/dist/core/base/create-root-observable.d.mts.map +1 -0
- package/dist/core/base/create-root-observable.mjs +36 -0
- package/dist/core/base/create-root-observable.mjs.map +1 -0
- package/dist/core/base/index.d.mts +3 -0
- package/dist/core/base/index.d.mts.map +1 -0
- package/dist/core/base/index.mjs +3 -0
- package/dist/core/{class → base}/index.mjs.map +1 -1
- package/dist/core/combine/combine.d.mts.map +1 -1
- package/dist/core/combine/combine.mjs +28 -27
- package/dist/core/combine/combine.mjs.map +1 -1
- package/dist/core/combine/merge.d.mts.map +1 -1
- package/dist/core/combine/merge.mjs +11 -13
- package/dist/core/combine/merge.mjs.map +1 -1
- package/dist/core/combine/zip.d.mts.map +1 -1
- package/dist/core/combine/zip.mjs +32 -31
- package/dist/core/combine/zip.mjs.map +1 -1
- package/dist/core/create/counter.d.mts.map +1 -1
- package/dist/core/create/counter.mjs +39 -50
- package/dist/core/create/counter.mjs.map +1 -1
- package/dist/core/create/from-abortable-promise.d.mts.map +1 -1
- package/dist/core/create/from-abortable-promise.mjs +33 -32
- package/dist/core/create/from-abortable-promise.mjs.map +1 -1
- package/dist/core/create/from-promise.d.mts.map +1 -1
- package/dist/core/create/from-promise.mjs +12 -12
- package/dist/core/create/from-promise.mjs.map +1 -1
- package/dist/core/create/from-subscribable.d.mts.map +1 -1
- package/dist/core/create/from-subscribable.mjs +10 -10
- package/dist/core/create/from-subscribable.mjs.map +1 -1
- package/dist/core/create/just.d.mts.map +1 -1
- package/dist/core/create/just.mjs +5 -4
- package/dist/core/create/just.mjs.map +1 -1
- package/dist/core/create/source.mjs +11 -12
- package/dist/core/create/source.mjs.map +1 -1
- package/dist/core/create/timer.d.mts.map +1 -1
- package/dist/core/create/timer.mjs +32 -38
- package/dist/core/create/timer.mjs.map +1 -1
- package/dist/core/index.d.mts +0 -1
- package/dist/core/index.d.mts.map +1 -1
- package/dist/core/index.mjs +0 -3
- package/dist/core/index.mjs.map +1 -1
- package/dist/core/operators/audit.d.mts.map +1 -1
- package/dist/core/operators/audit.mjs +28 -36
- package/dist/core/operators/audit.mjs.map +1 -1
- package/dist/core/operators/debounce.d.mts.map +1 -1
- package/dist/core/operators/debounce.mjs +26 -33
- package/dist/core/operators/debounce.mjs.map +1 -1
- package/dist/core/operators/filter.d.mts.map +1 -1
- package/dist/core/operators/filter.mjs +23 -27
- package/dist/core/operators/filter.mjs.map +1 -1
- package/dist/core/operators/map.d.mts.map +1 -1
- package/dist/core/operators/map.mjs +21 -28
- package/dist/core/operators/map.mjs.map +1 -1
- package/dist/core/operators/merge-map.d.mts.map +1 -1
- package/dist/core/operators/merge-map.mjs +30 -39
- package/dist/core/operators/merge-map.mjs.map +1 -1
- package/dist/core/operators/pairwise.d.mts.map +1 -1
- package/dist/core/operators/pairwise.mjs +22 -24
- package/dist/core/operators/pairwise.mjs.map +1 -1
- package/dist/core/operators/scan.d.mts.map +1 -1
- package/dist/core/operators/scan.mjs +13 -18
- package/dist/core/operators/scan.mjs.map +1 -1
- package/dist/core/operators/skip-if-no-change.d.mts.map +1 -1
- package/dist/core/operators/skip-if-no-change.mjs +22 -26
- package/dist/core/operators/skip-if-no-change.mjs.map +1 -1
- package/dist/core/operators/skip-until.d.mts.map +1 -1
- package/dist/core/operators/skip-until.mjs +23 -24
- package/dist/core/operators/skip-until.mjs.map +1 -1
- package/dist/core/operators/skip-while.d.mts.map +1 -1
- package/dist/core/operators/skip-while.mjs +27 -32
- package/dist/core/operators/skip-while.mjs.map +1 -1
- package/dist/core/operators/switch-map.d.mts.map +1 -1
- package/dist/core/operators/switch-map.mjs +28 -34
- package/dist/core/operators/switch-map.mjs.map +1 -1
- package/dist/core/operators/take-until.d.mts.map +1 -1
- package/dist/core/operators/take-until.mjs +22 -22
- package/dist/core/operators/take-until.mjs.map +1 -1
- package/dist/core/operators/take-while.d.mts.map +1 -1
- package/dist/core/operators/take-while.mjs +25 -29
- package/dist/core/operators/take-while.mjs.map +1 -1
- package/dist/core/operators/throttle.d.mts.map +1 -1
- package/dist/core/operators/throttle.mjs +27 -35
- package/dist/core/operators/throttle.mjs.map +1 -1
- package/dist/core/operators/with-buffered-from.d.mts.map +1 -1
- package/dist/core/operators/with-buffered-from.mjs +24 -24
- package/dist/core/operators/with-buffered-from.mjs.map +1 -1
- package/dist/core/operators/with-current-value-from.d.mts.map +1 -1
- package/dist/core/operators/with-current-value-from.mjs +20 -25
- package/dist/core/operators/with-current-value-from.mjs.map +1 -1
- package/dist/core/operators/with-initial-value.d.mts.map +1 -1
- package/dist/core/operators/with-initial-value.mjs +12 -15
- package/dist/core/operators/with-initial-value.mjs.map +1 -1
- package/dist/entry-point.mjs +0 -3
- package/dist/entry-point.mjs.map +1 -1
- package/dist/index.mjs +0 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +25 -18
- package/src/core/{class → base}/circular-dependency-comparison.test.mts +28 -18
- package/src/core/base/circular-dependency.test.mts +322 -0
- package/src/core/base/create-child-observable.mts +340 -0
- package/src/core/base/create-observable-base.mts +358 -0
- package/src/core/base/create-root-observable.mts +102 -0
- package/src/core/base/index.mts +2 -0
- package/src/core/{class/observable.class.test.mts → base/observable-base.test.mts} +25 -17
- package/src/core/combine/combine.mts +51 -53
- package/src/core/combine/merge.mts +21 -23
- package/src/core/combine/zip.mts +54 -58
- package/src/core/create/counter.mts +42 -59
- package/src/core/create/from-abortable-promise.mts +38 -45
- package/src/core/create/from-promise.mts +25 -28
- package/src/core/create/from-subscribable.mts +23 -27
- package/src/core/create/just.mts +5 -4
- package/src/core/create/source.mts +11 -17
- package/src/core/create/timer.mts +32 -47
- package/src/core/index.mts +0 -1
- package/src/core/operators/audit.mts +43 -57
- package/src/core/operators/debounce.mts +35 -45
- package/src/core/operators/filter.mts +31 -39
- package/src/core/operators/map.mts +32 -40
- package/src/core/operators/merge-map.mts +44 -59
- package/src/core/operators/pairwise.mts +30 -33
- package/src/core/operators/scan.mts +24 -35
- package/src/core/operators/skip-if-no-change.mts +31 -36
- package/src/core/operators/skip-until.mts +32 -35
- package/src/core/operators/skip-while.mts +31 -40
- package/src/core/operators/switch-map.mts +36 -48
- package/src/core/operators/take-until.mts +31 -29
- package/src/core/operators/take-while.mts +33 -41
- package/src/core/operators/throttle.mts +37 -51
- package/src/core/operators/with-buffered-from.mts +35 -30
- package/src/core/operators/with-current-value-from.mts +23 -28
- package/src/core/operators/with-initial-value.mts +21 -22
- package/dist/core/class/child-observable-class.d.mts +0 -39
- package/dist/core/class/child-observable-class.d.mts.map +0 -1
- package/dist/core/class/child-observable-class.mjs +0 -173
- package/dist/core/class/child-observable-class.mjs.map +0 -1
- package/dist/core/class/index.d.mts +0 -4
- package/dist/core/class/index.d.mts.map +0 -1
- package/dist/core/class/index.mjs +0 -4
- package/dist/core/class/observable-base-class.d.mts +0 -28
- package/dist/core/class/observable-base-class.d.mts.map +0 -1
- package/dist/core/class/observable-base-class.mjs +0 -120
- package/dist/core/class/observable-base-class.mjs.map +0 -1
- package/dist/core/class/root-observable-class.d.mts +0 -14
- package/dist/core/class/root-observable-class.d.mts.map +0 -1
- package/dist/core/class/root-observable-class.mjs +0 -43
- package/dist/core/class/root-observable-class.mjs.map +0 -1
- package/src/core/class/child-observable-class.mts +0 -286
- package/src/core/class/circular-dependency.test.mts +0 -251
- package/src/core/class/index.mts +0 -3
- package/src/core/class/observable-base-class.mts +0 -187
- package/src/core/class/root-observable-class.mts +0 -73
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"with-buffered-from.d.mts","sourceRoot":"","sources":["../../../src/core/operators/with-buffered-from.mts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,wBAAwB,EAC7B,KAAK,UAAU,
|
|
1
|
+
{"version":3,"file":"with-buffered-from.d.mts","sourceRoot":"","sources":["../../../src/core/operators/with-buffered-from.mts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,wBAAwB,EAC7B,KAAK,UAAU,EAEhB,MAAM,oBAAoB,CAAC;AAG5B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,eAAO,MAAM,gBAAgB,GAAI,CAAC,EAAE,CAAC,cACvB,UAAU,CAAC,CAAC,CAAC,KACxB,wBAAwB,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,EAAE,SAAS,CAAC,EAAE,CAAC,CAMQ,CAAC;AAElE;;;GAGG;AACH,eAAO,MAAM,YAAY,yBAAmB,CAAC"}
|
|
@@ -1,20 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Optional, pipe, Arr } from 'ts-data-forge';
|
|
2
|
+
import { createSyncChildObservable } from '../base/create-child-observable.mjs';
|
|
3
3
|
import '../utils/id-maker.mjs';
|
|
4
4
|
import { maxDepth } from '../utils/max-depth.mjs';
|
|
5
5
|
|
|
6
6
|
const withBufferedFrom = (observable) => (
|
|
7
7
|
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
8
|
-
((parentObservable) =>
|
|
8
|
+
((parentObservable) => createWithBufferedFromObservable(
|
|
9
9
|
parentObservable,
|
|
10
10
|
observable
|
|
11
11
|
))
|
|
12
12
|
);
|
|
13
13
|
const withBuffered = withBufferedFrom;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
const createWithBufferedFromObservable = (parentObservable, observable) => {
|
|
15
|
+
let mut_bufferedValues = [];
|
|
16
|
+
const clearBuffer = () => {
|
|
17
|
+
mut_bufferedValues = [];
|
|
18
|
+
};
|
|
19
|
+
const result = createSyncChildObservable(
|
|
20
|
+
{
|
|
18
21
|
parents: [parentObservable],
|
|
19
22
|
depth: 1 + maxDepth([parentObservable, observable]),
|
|
20
23
|
initialValue: pipe({
|
|
@@ -26,24 +29,21 @@ class WithBufferedFromObservableClass extends SyncChildObservableClass {
|
|
|
26
29
|
Optional.isNone(me) ? [] : [me.value]
|
|
27
30
|
])
|
|
28
31
|
).value
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
tryUpdate(updateToken) {
|
|
38
|
-
const par = this.parents[0];
|
|
39
|
-
const sn = par.getSnapshot();
|
|
40
|
-
if (par.updateToken !== updateToken || Optional.isNone(sn)) {
|
|
41
|
-
return;
|
|
32
|
+
},
|
|
33
|
+
({ setNext }) => (updateToken) => {
|
|
34
|
+
const sn = parentObservable.getSnapshot();
|
|
35
|
+
if (parentObservable.updateToken !== updateToken || Optional.isNone(sn)) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
setNext([sn.value, mut_bufferedValues], updateToken);
|
|
39
|
+
clearBuffer();
|
|
42
40
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
41
|
+
);
|
|
42
|
+
observable.subscribe((value) => {
|
|
43
|
+
mut_bufferedValues = Arr.toPushed(mut_bufferedValues, value);
|
|
44
|
+
});
|
|
45
|
+
return result;
|
|
46
|
+
};
|
|
47
47
|
|
|
48
48
|
export { withBuffered, withBufferedFrom };
|
|
49
49
|
//# sourceMappingURL=with-buffered-from.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"with-buffered-from.mjs","sources":["../../../src/core/operators/with-buffered-from.mts"],"sourcesContent":["import { Arr, Optional, pipe } from 'ts-data-forge';\nimport {
|
|
1
|
+
{"version":3,"file":"with-buffered-from.mjs","sources":["../../../src/core/operators/with-buffered-from.mts"],"sourcesContent":["import { Arr, Optional, pipe } from 'ts-data-forge';\nimport { createSyncChildObservable } from '../base/index.mjs';\nimport {\n type KeepInitialValueOperator,\n type Observable,\n type WithBufferedFromOperatorObservable,\n} from '../types/index.mjs';\nimport { maxDepth } from '../utils/index.mjs';\n\n/**\n * Buffers values from the source observable and emits them along with the parent value\n * when the parent emits. The buffer is cleared after each emission.\n *\n * @template A - The type of values from the parent observable\n * @template B - The type of values from the source observable\n * @param observable - The observable whose values will be buffered\n * @returns An operator that emits tuples of [parentValue, bufferedValues]\n *\n * @example\n * ```ts\n * // Timeline:\n * //\n * // data$ d1 d2 d3 d4 d5 d6 d7 d8\n * // trigger$ T1 T2 T3\n * // result$ [T1,[d1,d2,d3]] [T2,[d4,d5,d6]] [T3,[d7,d8]]\n * //\n * // Explanation:\n * // - withBufferedFrom collects values from the source observable\n * // - When the trigger emits, it emits a tuple of [triggerValue, bufferedValues]\n * // - Buffer is cleared after each emission\n * // - Useful for batching data collection triggered by events\n *\n * const data$ = source<string>();\n *\n * const trigger$ = source<number>();\n *\n * const result$ = trigger$.pipe(withBufferedFrom(data$));\n *\n * const valueHistory: (readonly [number, readonly string[]])[] = [];\n *\n * result$.subscribe(([triggerValue, bufferedData]) => {\n * valueHistory.push([triggerValue, bufferedData]);\n * });\n *\n * data$.next('A');\n *\n * data$.next('B');\n *\n * trigger$.next(1);\n *\n * assert.deepStrictEqual(valueHistory, [[1, ['A', 'B']]]);\n *\n * data$.next('C');\n *\n * trigger$.next(2);\n *\n * assert.deepStrictEqual(valueHistory, [\n * [1, ['A', 'B']],\n * [2, ['C']],\n * ]);\n * ```\n */\nexport const withBufferedFrom = <A, B>(\n observable: Observable<B>,\n): KeepInitialValueOperator<A, readonly [A, readonly B[]]> =>\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n ((parentObservable) =>\n createWithBufferedFromObservable(\n parentObservable,\n observable,\n )) as KeepInitialValueOperator<A, readonly [A, readonly B[]]>;\n\n/**\n * Alias for `withBufferedFrom`.\n * @see withBufferedFrom\n */\nexport const withBuffered = withBufferedFrom;\n\nconst createWithBufferedFromObservable = <A, B>(\n parentObservable: Observable<A>,\n observable: Observable<B>,\n): WithBufferedFromOperatorObservable<A, B> => {\n let mut_bufferedValues: readonly B[] = [];\n\n const clearBuffer = (): void => {\n mut_bufferedValues = [];\n };\n\n const result = createSyncChildObservable<\n readonly [A, readonly B[]],\n readonly [A]\n >(\n {\n parents: [parentObservable],\n depth: 1 + maxDepth([parentObservable, observable]),\n initialValue: pipe({\n par: parentObservable.getSnapshot(),\n me: observable.getSnapshot(),\n }).map(({ par, me }) =>\n Optional.isNone(par)\n ? Optional.none\n : Optional.some([\n par.value,\n Optional.isNone(me) ? [] : [me.value],\n ] as const),\n ).value,\n },\n ({ setNext }) =>\n (updateToken) => {\n const sn = parentObservable.getSnapshot();\n\n if (\n parentObservable.updateToken !== updateToken ||\n Optional.isNone(sn)\n ) {\n return; // skip update\n }\n\n setNext([sn.value, mut_bufferedValues], updateToken);\n\n clearBuffer();\n },\n );\n\n observable.subscribe((value) => {\n mut_bufferedValues = Arr.toPushed(mut_bufferedValues, value);\n });\n\n return result;\n};\n"],"names":[],"mappings":";;;;;AA8DO,MAAM,mBAAmB,CAC9B,UAAA;AAAA;AAAA,GAGC,CAAC,gBAAA,KACA,gCAAA;AAAA,IACE,gBAAA;AAAA,IACA;AAAA,GACF;AAAA;AAMG,MAAM,YAAA,GAAe;AAE5B,MAAM,gCAAA,GAAmC,CACvC,gBAAA,EACA,UAAA,KAC6C;AAC7C,EAAA,IAAI,qBAAmC,EAAC;AAExC,EAAA,MAAM,cAAc,MAAY;AAC9B,IAAA,kBAAA,GAAqB,EAAC;AAAA,EACxB,CAAA;AAEA,EAAA,MAAM,MAAA,GAAS,yBAAA;AAAA,IAIb;AAAA,MACE,OAAA,EAAS,CAAC,gBAAgB,CAAA;AAAA,MAC1B,OAAO,CAAA,GAAI,QAAA,CAAS,CAAC,gBAAA,EAAkB,UAAU,CAAC,CAAA;AAAA,MAClD,cAAc,IAAA,CAAK;AAAA,QACjB,GAAA,EAAK,iBAAiB,WAAA,EAAY;AAAA,QAClC,EAAA,EAAI,WAAW,WAAA;AAAY,OAC5B,CAAA,CAAE,GAAA;AAAA,QAAI,CAAC,EAAE,GAAA,EAAK,EAAA,EAAG,KAChB,QAAA,CAAS,MAAA,CAAO,GAAG,CAAA,GACf,QAAA,CAAS,IAAA,GACT,QAAA,CAAS,IAAA,CAAK;AAAA,UACZ,GAAA,CAAI,KAAA;AAAA,UACJ,QAAA,CAAS,OAAO,EAAE,CAAA,GAAI,EAAC,GAAI,CAAC,GAAG,KAAK;AAAA,SAC5B;AAAA,OAChB,CAAE;AAAA,KACJ;AAAA,IACA,CAAC,EAAE,OAAA,EAAQ,KACT,CAAC,WAAA,KAAgB;AACf,MAAA,MAAM,EAAA,GAAK,iBAAiB,WAAA,EAAY;AAExC,MAAA,IACE,iBAAiB,WAAA,KAAgB,WAAA,IACjC,QAAA,CAAS,MAAA,CAAO,EAAE,CAAA,EAClB;AACA,QAAA;AAAA,MACF;AAEA,MAAA,OAAA,CAAQ,CAAC,EAAA,CAAG,KAAA,EAAO,kBAAkB,GAAG,WAAW,CAAA;AAEnD,MAAA,WAAA,EAAY;AAAA,IACd;AAAA,GACJ;AAEA,EAAA,UAAA,CAAW,SAAA,CAAU,CAAC,KAAA,KAAU;AAC9B,IAAA,kBAAA,GAAqB,GAAA,CAAI,QAAA,CAAS,kBAAA,EAAoB,KAAK,CAAA;AAAA,EAC7D,CAAC,CAAA;AAED,EAAA,OAAO,MAAA;AACT,CAAA;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"with-current-value-from.d.mts","sourceRoot":"","sources":["../../../src/core/operators/with-current-value-from.mts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,wBAAwB,EAC7B,KAAK,UAAU,
|
|
1
|
+
{"version":3,"file":"with-current-value-from.d.mts","sourceRoot":"","sources":["../../../src/core/operators/with-current-value-from.mts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,wBAAwB,EAC7B,KAAK,UAAU,EAEhB,MAAM,oBAAoB,CAAC;AAG5B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,eAAO,MAAM,oBAAoB,GAC9B,CAAC,EAAE,CAAC,cACS,UAAU,CAAC,CAAC,CAAC,KACxB,wBAAwB,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAEsB,CAAC;AAEvE;;;GAGG;AACH,eAAO,MAAM,cAAc,6BAAuB,CAAC"}
|
|
@@ -1,36 +1,31 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Optional, pipe } from 'ts-data-forge';
|
|
2
|
+
import { createSyncChildObservable } from '../base/create-child-observable.mjs';
|
|
3
3
|
import '../utils/id-maker.mjs';
|
|
4
4
|
import { maxDepth } from '../utils/max-depth.mjs';
|
|
5
5
|
|
|
6
|
-
const withCurrentValueFrom = (observable) => (parentObservable) =>
|
|
6
|
+
const withCurrentValueFrom = (observable) => (parentObservable) => createWithCurrentValueFromObservable(parentObservable, observable);
|
|
7
7
|
const withLatestFrom = withCurrentValueFrom;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
tryUpdate(updateToken) {
|
|
24
|
-
const par = this.parents[0];
|
|
25
|
-
const ps = par.getSnapshot();
|
|
26
|
-
if (par.updateToken !== updateToken || Optional.isNone(ps)) {
|
|
8
|
+
const createWithCurrentValueFromObservable = (parentObservable, observable) => createSyncChildObservable(
|
|
9
|
+
{
|
|
10
|
+
parents: [parentObservable],
|
|
11
|
+
depth: 1 + maxDepth([parentObservable, observable]),
|
|
12
|
+
initialValue: pipe({
|
|
13
|
+
par: parentObservable.getSnapshot(),
|
|
14
|
+
me: observable.getSnapshot()
|
|
15
|
+
}).map(
|
|
16
|
+
({ me, par }) => Optional.isNone(par) || Optional.isNone(me) ? Optional.none : Optional.some([par.value, me.value])
|
|
17
|
+
).value
|
|
18
|
+
},
|
|
19
|
+
({ setNext }) => (updateToken) => {
|
|
20
|
+
const ps = parentObservable.getSnapshot();
|
|
21
|
+
if (parentObservable.updateToken !== updateToken || Optional.isNone(ps)) {
|
|
27
22
|
return;
|
|
28
23
|
}
|
|
29
|
-
const curr =
|
|
24
|
+
const curr = observable.getSnapshot();
|
|
30
25
|
if (Optional.isNone(curr)) return;
|
|
31
|
-
|
|
26
|
+
setNext([ps.value, curr.value], updateToken);
|
|
32
27
|
}
|
|
33
|
-
|
|
28
|
+
);
|
|
34
29
|
|
|
35
30
|
export { withCurrentValueFrom, withLatestFrom };
|
|
36
31
|
//# sourceMappingURL=with-current-value-from.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"with-current-value-from.mjs","sources":["../../../src/core/operators/with-current-value-from.mts"],"sourcesContent":["import { Optional, pipe } from 'ts-data-forge';\nimport {
|
|
1
|
+
{"version":3,"file":"with-current-value-from.mjs","sources":["../../../src/core/operators/with-current-value-from.mts"],"sourcesContent":["import { Optional, pipe } from 'ts-data-forge';\nimport { createSyncChildObservable } from '../base/index.mjs';\nimport {\n type DropInitialValueOperator,\n type Observable,\n type WithCurrentValueFromOperatorObservable,\n} from '../types/index.mjs';\nimport { maxDepth } from '../utils/index.mjs';\n\n/**\n * Samples the current value from another observable each time the source emits.\n * Emits a tuple of [sourceValue, sampledValue].\n *\n * @template A - The type of values from the source observable\n * @template B - The type of values from the sampled observable\n * @param observable - The observable to sample from\n * @returns An operator that emits tuples of source and sampled values\n *\n * @example\n * ```ts\n * // Timeline:\n * //\n * // name$ \"Alice\" \"Bob\" \"Charlie\"\n * // age$ 25 30\n * // result$ (skip) [\"Bob\",25] [\"Charlie\",30]\n * //\n * // Explanation:\n * // - withCurrentValueFrom samples the current value from another observable\n * // - Emits a tuple [sourceValue, sampledValue] each time the SOURCE emits\n * // - Does NOT emit when the sampled observable (age$) emits\n * // - Does not emit until both observables have emitted at least once\n * // - \"Alice\" is skipped because age$ hasn't emitted yet\n *\n * const name$ = source<string>();\n *\n * const age$ = source<number>();\n *\n * const result$ = name$.pipe(withCurrentValueFrom(age$));\n *\n * const valueHistory: (readonly [string, number])[] = [];\n *\n * result$.subscribe(([name_, currentAge]) => {\n * valueHistory.push([name_, currentAge]);\n * });\n *\n * name$.next('Alice'); // nothing logged (age$ hasn't emitted)\n *\n * assert.deepStrictEqual(valueHistory, []);\n *\n * age$.next(25);\n *\n * name$.next('Bob'); // logs: Bob is 25 years old\n *\n * assert.deepStrictEqual(valueHistory, [['Bob', 25]]);\n *\n * age$.next(30);\n *\n * name$.next('Charlie'); // logs: Charlie is 30 years old\n *\n * assert.deepStrictEqual(valueHistory, [\n * ['Bob', 25],\n * ['Charlie', 30],\n * ]);\n * ```\n */\nexport const withCurrentValueFrom =\n <A, B>(\n observable: Observable<B>,\n ): DropInitialValueOperator<A, readonly [A, B]> =>\n (parentObservable) =>\n createWithCurrentValueFromObservable(parentObservable, observable);\n\n/**\n * Alias for `withCurrentValueFrom`.\n * @see withCurrentValueFrom\n */\nexport const withLatestFrom = withCurrentValueFrom;\n\nconst createWithCurrentValueFromObservable = <A, B>(\n parentObservable: Observable<A>,\n observable: Observable<B>,\n): WithCurrentValueFromOperatorObservable<A, B> =>\n createSyncChildObservable<readonly [A, B], readonly [A]>(\n {\n parents: [parentObservable],\n depth: 1 + maxDepth([parentObservable, observable]),\n initialValue: pipe({\n par: parentObservable.getSnapshot(),\n me: observable.getSnapshot(),\n }).map(({ me, par }) =>\n Optional.isNone(par) || Optional.isNone(me)\n ? Optional.none\n : Optional.some([par.value, me.value] as const),\n ).value,\n },\n ({ setNext }) =>\n (updateToken) => {\n const ps = parentObservable.getSnapshot();\n\n if (\n parentObservable.updateToken !== updateToken ||\n Optional.isNone(ps)\n ) {\n return; // skip update\n }\n\n const curr = observable.getSnapshot();\n\n if (Optional.isNone(curr)) return; // skip update\n\n setNext([ps.value, curr.value], updateToken);\n },\n );\n"],"names":[],"mappings":";;;;;AAiEO,MAAM,uBACX,CACE,UAAA,KAEF,CAAC,gBAAA,KACC,oCAAA,CAAqC,kBAAkB,UAAU;AAM9D,MAAM,cAAA,GAAiB;AAE9B,MAAM,oCAAA,GAAuC,CAC3C,gBAAA,EACA,UAAA,KAEA,yBAAA;AAAA,EACE;AAAA,IACE,OAAA,EAAS,CAAC,gBAAgB,CAAA;AAAA,IAC1B,OAAO,CAAA,GAAI,QAAA,CAAS,CAAC,gBAAA,EAAkB,UAAU,CAAC,CAAA;AAAA,IAClD,cAAc,IAAA,CAAK;AAAA,MACjB,GAAA,EAAK,iBAAiB,WAAA,EAAY;AAAA,MAClC,EAAA,EAAI,WAAW,WAAA;AAAY,KAC5B,CAAA,CAAE,GAAA;AAAA,MAAI,CAAC,EAAE,EAAA,EAAI,GAAA,OACZ,QAAA,CAAS,MAAA,CAAO,GAAG,CAAA,IAAK,QAAA,CAAS,MAAA,CAAO,EAAE,CAAA,GACtC,QAAA,CAAS,OACT,QAAA,CAAS,IAAA,CAAK,CAAC,GAAA,CAAI,KAAA,EAAO,EAAA,CAAG,KAAK,CAAU;AAAA,KAClD,CAAE;AAAA,GACJ;AAAA,EACA,CAAC,EAAE,OAAA,EAAQ,KACT,CAAC,WAAA,KAAgB;AACf,IAAA,MAAM,EAAA,GAAK,iBAAiB,WAAA,EAAY;AAExC,IAAA,IACE,iBAAiB,WAAA,KAAgB,WAAA,IACjC,QAAA,CAAS,MAAA,CAAO,EAAE,CAAA,EAClB;AACA,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,IAAA,GAAO,WAAW,WAAA,EAAY;AAEpC,IAAA,IAAI,QAAA,CAAS,MAAA,CAAO,IAAI,CAAA,EAAG;AAE3B,IAAA,OAAA,CAAQ,CAAC,EAAA,CAAG,KAAA,EAAO,IAAA,CAAK,KAAK,GAAG,WAAW,CAAA;AAAA,EAC7C;AACJ,CAAA;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"with-initial-value.d.mts","sourceRoot":"","sources":["../../../src/core/operators/with-initial-value.mts"],"names":[],"mappings":"AAGA,OAAO,
|
|
1
|
+
{"version":3,"file":"with-initial-value.d.mts","sourceRoot":"","sources":["../../../src/core/operators/with-initial-value.mts"],"names":[],"mappings":"AAGA,OAAO,EAGL,KAAK,wBAAwB,EAE9B,MAAM,oBAAoB,CAAC;AAE5B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,eAAO,MAAM,gBAAgB,GAC1B,CAAC,EAAE,CAAC,GAAG,CAAC,gBAAgB,CAAC,KAAG,wBAAwB,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAEI,CAAC"}
|
|
@@ -1,24 +1,21 @@
|
|
|
1
1
|
import { Optional } from 'ts-data-forge';
|
|
2
|
-
import {
|
|
2
|
+
import { createInitializedSyncChildObservable } from '../base/create-child-observable.mjs';
|
|
3
3
|
import '../utils/id-maker.mjs';
|
|
4
4
|
|
|
5
|
-
const withInitialValue = (initialValue) => (parentObservable) =>
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const par = this.parents[0];
|
|
15
|
-
const sn = par.getSnapshot();
|
|
16
|
-
if (par.updateToken !== updateToken || Optional.isNone(sn)) {
|
|
5
|
+
const withInitialValue = (initialValue) => (parentObservable) => createWithInitialValueObservable(parentObservable, initialValue);
|
|
6
|
+
const createWithInitialValueObservable = (parentObservable, initialValue) => createInitializedSyncChildObservable(
|
|
7
|
+
{
|
|
8
|
+
parents: [parentObservable],
|
|
9
|
+
initialValue: Optional.some(initialValue)
|
|
10
|
+
},
|
|
11
|
+
({ setNext }) => (updateToken) => {
|
|
12
|
+
const sn = parentObservable.getSnapshot();
|
|
13
|
+
if (parentObservable.updateToken !== updateToken || Optional.isNone(sn)) {
|
|
17
14
|
return;
|
|
18
15
|
}
|
|
19
|
-
|
|
16
|
+
setNext(sn.value, updateToken);
|
|
20
17
|
}
|
|
21
|
-
|
|
18
|
+
);
|
|
22
19
|
|
|
23
20
|
export { withInitialValue };
|
|
24
21
|
//# sourceMappingURL=with-initial-value.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"with-initial-value.mjs","sources":["../../../src/core/operators/with-initial-value.mts"],"sourcesContent":["import { expectType, Optional } from 'ts-data-forge';\nimport {
|
|
1
|
+
{"version":3,"file":"with-initial-value.mjs","sources":["../../../src/core/operators/with-initial-value.mts"],"sourcesContent":["import { expectType, Optional } from 'ts-data-forge';\nimport { createInitializedSyncChildObservable } from '../base/index.mjs';\nimport { source } from '../create/index.mjs';\nimport {\n type InitializedObservable,\n type Observable,\n type WithInitialValueOperator,\n type WithInitialValueOperatorObservable,\n} from '../types/index.mjs';\n\n/**\n * Provides an initial value for an observable that doesn't have one.\n * The resulting observable will immediately emit the initial value upon subscription,\n * and then emit all subsequent values from the source.\n *\n * @template A - The type of values from the source\n * @template I - The type of the initial value (defaults to A)\n * @param initialValue - The initial value to emit\n * @returns An operator that sets the initial value\n *\n * @example\n * ```ts\n * // Timeline:\n * //\n * // num$ 1 2 3\n * // withInitial$ 0 1 2 3\n * // ^\n * // initial value\n * //\n * // Explanation:\n * // - withInitialValue provides an initial value before the source emits\n * // - Converts an uninitialized observable to an initialized one\n * // - Useful when you need a default value immediately\n *\n * const num$ = source<number>();\n *\n * const initialized$ = num$.pipe(withInitialValue(0));\n *\n * const valueHistory: number[] = [];\n *\n * initialized$.subscribe((x) => {\n * valueHistory.push(x);\n * });\n *\n * assert.deepStrictEqual(valueHistory, [0]);\n *\n * num$.next(1); // logs: 1\n *\n * assert.deepStrictEqual(valueHistory, [0, 1]);\n *\n * num$.next(2); // logs: 2\n *\n * assert.deepStrictEqual(valueHistory, [0, 1, 2]);\n * ```\n */\nexport const withInitialValue =\n <A, I = A>(initialValue: I): WithInitialValueOperator<A, A | I> =>\n (parentObservable) =>\n createWithInitialValueObservable(parentObservable, initialValue);\n\nconst createWithInitialValueObservable = <A, I>(\n parentObservable: Observable<A>,\n initialValue: I,\n): WithInitialValueOperatorObservable<A, I> =>\n createInitializedSyncChildObservable<A | I, readonly [A]>(\n {\n parents: [parentObservable],\n initialValue: Optional.some(initialValue),\n },\n ({ setNext }) =>\n (updateToken) => {\n const sn = parentObservable.getSnapshot();\n\n if (\n parentObservable.updateToken !== updateToken ||\n Optional.isNone(sn)\n ) {\n return; // skip update\n }\n\n setNext(sn.value, updateToken);\n },\n );\n\nif (import.meta.vitest !== undefined) {\n test('type test', () => {\n expect(1).toBe(1); // dummy\n });\n\n {\n const s = source<number>();\n\n const _d: InitializedObservable<number> = s.pipe(withInitialValue(0));\n\n expectType<typeof _d, InitializedObservable<number>>('=');\n }\n}\n"],"names":[],"mappings":";;;;AAuDO,MAAM,mBACX,CAAW,YAAA,KACX,CAAC,gBAAA,KACC,gCAAA,CAAiC,kBAAkB,YAAY;AAEnE,MAAM,gCAAA,GAAmC,CACvC,gBAAA,EACA,YAAA,KAEA,oCAAA;AAAA,EACE;AAAA,IACE,OAAA,EAAS,CAAC,gBAAgB,CAAA;AAAA,IAC1B,YAAA,EAAc,QAAA,CAAS,IAAA,CAAK,YAAY;AAAA,GAC1C;AAAA,EACA,CAAC,EAAE,OAAA,EAAQ,KACT,CAAC,WAAA,KAAgB;AACf,IAAA,MAAM,EAAA,GAAK,iBAAiB,WAAA,EAAY;AAExC,IAAA,IACE,iBAAiB,WAAA,KAAgB,WAAA,IACjC,QAAA,CAAS,MAAA,CAAO,EAAE,CAAA,EAClB;AACA,MAAA;AAAA,IAAA;AAGF,IAAA,OAAA,CAAQ,EAAA,CAAG,OAAO,WAAW,CAAA;AAAA,EAAA;AAEnC,CAAA;;;;"}
|
package/dist/entry-point.mjs
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
export { AsyncChildObservableClass, InitializedSyncChildObservableClass, SyncChildObservableClass } from './core/class/child-observable-class.mjs';
|
|
2
|
-
export { ObservableBaseClass } from './core/class/observable-base-class.mjs';
|
|
3
|
-
export { RootObservableClass } from './core/class/root-observable-class.mjs';
|
|
4
1
|
export { combine, combineLatest } from './core/combine/combine.mjs';
|
|
5
2
|
export { merge } from './core/combine/merge.mjs';
|
|
6
3
|
export { zip } from './core/combine/zip.mjs';
|
package/dist/entry-point.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entry-point.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"entry-point.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
export { AsyncChildObservableClass, InitializedSyncChildObservableClass, SyncChildObservableClass } from './core/class/child-observable-class.mjs';
|
|
2
|
-
export { ObservableBaseClass } from './core/class/observable-base-class.mjs';
|
|
3
|
-
export { RootObservableClass } from './core/class/root-observable-class.mjs';
|
|
4
1
|
export { combine, combineLatest } from './core/combine/combine.mjs';
|
|
5
2
|
export { merge } from './core/combine/merge.mjs';
|
|
6
3
|
export { zip } from './core/combine/zip.mjs';
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "synstate",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Type-safe State Management Library for TypeScript/JavaScript",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
},
|
|
22
|
-
"module": "./dist/index.
|
|
23
|
-
"types": "./dist/index.d.
|
|
22
|
+
"module": "./dist/index.mjs",
|
|
23
|
+
"types": "./dist/index.d.mts",
|
|
24
24
|
"files": [
|
|
25
25
|
"src",
|
|
26
26
|
"dist",
|
|
@@ -29,33 +29,40 @@
|
|
|
29
29
|
"LICENSE"
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"ts-data-forge": "^14.
|
|
33
|
-
"ts-type-forge": "^9.2.
|
|
32
|
+
"ts-data-forge": "^14.5.0",
|
|
33
|
+
"ts-type-forge": "^9.2.2"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@reduxjs/toolkit": "2.12.0",
|
|
37
|
-
"@rollup/plugin-replace": "6.0.3",
|
|
38
|
-
"@rollup/plugin-strip": "3.0.4",
|
|
39
37
|
"@types/react": "19.2.18",
|
|
38
|
+
"@vitest/browser-playwright": "4.1.11",
|
|
40
39
|
"argparse": "3.0.0",
|
|
40
|
+
"eslint-config-typed": "5.10.0",
|
|
41
|
+
"eslint-plugin-ts-data-forge": "0.5.1",
|
|
42
|
+
"eslint-plugin-ts-fortress": "1.0.5",
|
|
43
|
+
"eslint-plugin-ts-type-forge": "0.7.1",
|
|
41
44
|
"jotai": "2.20.2",
|
|
42
|
-
"mobx": "7.0.
|
|
45
|
+
"mobx": "7.0.3",
|
|
46
|
+
"playwright": "1.62.1",
|
|
43
47
|
"prettier": "3.9.6",
|
|
44
48
|
"prettier-plugin-organize-imports": "4.3.0",
|
|
45
49
|
"prettier-plugin-packagejson": "3.0.2",
|
|
46
50
|
"react": "19.2.8",
|
|
47
51
|
"rollup": "4.62.4",
|
|
48
|
-
"rollup-plugin-esbuild": "6.2.1",
|
|
49
52
|
"rxjs": "7.8.2",
|
|
53
|
+
"synstate-react-hooks": "3.0.0",
|
|
54
|
+
"synstate-react-hooks-compat": "3.0.0",
|
|
55
|
+
"ts-repo-utils": "10.3.2",
|
|
50
56
|
"valtio": "2.3.2",
|
|
51
|
-
"zustand": "5.0.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
"
|
|
57
|
+
"zustand": "5.0.15"
|
|
58
|
+
},
|
|
59
|
+
"engines": {
|
|
60
|
+
"node": ">=22.22.2",
|
|
61
|
+
"pnpm": ">=9.0.0"
|
|
62
|
+
},
|
|
63
|
+
"publishConfig": {
|
|
64
|
+
"access": "public",
|
|
65
|
+
"provenance": true
|
|
59
66
|
},
|
|
60
67
|
"scripts": {
|
|
61
68
|
"benchmark": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/cmd/run-benchmark.mts",
|
|
@@ -73,7 +80,7 @@
|
|
|
73
80
|
"fmt:diff": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../../libs/ts-repo-utils/src/cmd/format-diff-from.mts origin/main --cwd .",
|
|
74
81
|
"fmt:full": "pnpm run z:prettier .",
|
|
75
82
|
"gi": "run-s gi:src fmt",
|
|
76
|
-
"gi:src": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../../libs/ts-repo-utils/src/cmd/gen-index-ts.mts ./src --index-ext .mts --export-ext .mjs --target-ext .mts --target-ext .tsx --exclude entry-point.mts --exclude \"core/utils/**\"",
|
|
83
|
+
"gi:src": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../../libs/ts-repo-utils/src/cmd/gen-index-ts.mts ./src --index-ext .mts --export-ext .mjs --target-ext .mts --target-ext .tsx --exclude entry-point.mts --exclude \"core/base/**\" --exclude \"core/utils/**\"",
|
|
77
84
|
"lint": "eslint .",
|
|
78
85
|
"lint:fix": "eslint . --fix",
|
|
79
86
|
"test": "pnpm run z:vitest:node run",
|
|
@@ -8,32 +8,39 @@
|
|
|
8
8
|
* only at read time as a `Maximum call stack size exceeded` error.
|
|
9
9
|
*/
|
|
10
10
|
/* eslint-disable functional/immutable-data */
|
|
11
|
-
/* eslint-disable no-new */
|
|
12
11
|
import { type Atom, atom, createStore } from 'jotai';
|
|
13
12
|
import { BehaviorSubject, combineLatest, map as rxMap } from 'rxjs';
|
|
14
13
|
import { Optional } from 'ts-data-forge';
|
|
15
14
|
import { combine } from '../combine/index.mjs';
|
|
16
15
|
import { source } from '../create/index.mjs';
|
|
17
16
|
import { map } from '../operators/index.mjs';
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
17
|
+
import { createSyncChildObservable } from './create-child-observable.mjs';
|
|
18
|
+
import { createTryUpdateNotImplemented } from './create-observable-base.mjs';
|
|
19
|
+
import { createRootObservable } from './create-root-observable.mjs';
|
|
20
20
|
|
|
21
21
|
describe('circular dependency comparison', () => {
|
|
22
22
|
describe('SynState', () => {
|
|
23
23
|
test('detects cycle at construction time with a clear error message', () => {
|
|
24
|
-
const root =
|
|
25
|
-
initialValue: Optional.some(0),
|
|
26
|
-
|
|
24
|
+
const root = createRootObservable(
|
|
25
|
+
{ initialValue: Optional.some(0) },
|
|
26
|
+
() => ({}),
|
|
27
|
+
);
|
|
27
28
|
|
|
28
|
-
const childA =
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
const childA = createSyncChildObservable(
|
|
30
|
+
{
|
|
31
|
+
parents: [root],
|
|
32
|
+
initialValue: Optional.some(0),
|
|
33
|
+
},
|
|
34
|
+
createTryUpdateNotImplemented,
|
|
35
|
+
);
|
|
32
36
|
|
|
33
|
-
const childB =
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
const childB = createSyncChildObservable(
|
|
38
|
+
{
|
|
39
|
+
parents: [childA],
|
|
40
|
+
initialValue: Optional.some(0),
|
|
41
|
+
},
|
|
42
|
+
createTryUpdateNotImplemented,
|
|
43
|
+
);
|
|
37
44
|
|
|
38
45
|
// Simulate a cycle: childA → childB → childA
|
|
39
46
|
// In normal usage this is impossible — the check runs in every
|
|
@@ -45,10 +52,13 @@ describe('circular dependency comparison', () => {
|
|
|
45
52
|
});
|
|
46
53
|
|
|
47
54
|
expect(() => {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
55
|
+
createSyncChildObservable(
|
|
56
|
+
{
|
|
57
|
+
parents: [childA],
|
|
58
|
+
initialValue: Optional.some(0),
|
|
59
|
+
},
|
|
60
|
+
createTryUpdateNotImplemented,
|
|
61
|
+
);
|
|
52
62
|
}).toThrow(
|
|
53
63
|
'Circular dependency detected in observable graph: a child observable cannot be its own ancestor.',
|
|
54
64
|
);
|