synstate 0.1.0 → 0.1.1
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/LICENSE +201 -0
- package/README.md +124 -350
- package/assets/synstate-icon.png +0 -0
- package/dist/core/combine/combine.d.mts +32 -2
- package/dist/core/combine/combine.d.mts.map +1 -1
- package/dist/core/combine/combine.mjs +32 -2
- package/dist/core/combine/combine.mjs.map +1 -1
- package/dist/core/combine/merge.d.mts +30 -4
- package/dist/core/combine/merge.d.mts.map +1 -1
- package/dist/core/combine/merge.mjs +30 -4
- package/dist/core/combine/merge.mjs.map +1 -1
- package/dist/core/combine/zip.d.mts +28 -3
- package/dist/core/combine/zip.d.mts.map +1 -1
- package/dist/core/combine/zip.mjs +28 -3
- package/dist/core/combine/zip.mjs.map +1 -1
- package/dist/core/create/from-array.d.mts +21 -3
- package/dist/core/create/from-array.d.mts.map +1 -1
- package/dist/core/create/from-array.mjs +21 -3
- package/dist/core/create/from-array.mjs.map +1 -1
- package/dist/core/create/from-promise.d.mts +29 -7
- package/dist/core/create/from-promise.d.mts.map +1 -1
- package/dist/core/create/from-promise.mjs +29 -7
- package/dist/core/create/from-promise.mjs.map +1 -1
- package/dist/core/create/from-subscribable.d.mts +58 -0
- package/dist/core/create/from-subscribable.d.mts.map +1 -1
- package/dist/core/create/from-subscribable.mjs +58 -0
- package/dist/core/create/from-subscribable.mjs.map +1 -1
- package/dist/core/create/interval.d.mts +29 -4
- package/dist/core/create/interval.d.mts.map +1 -1
- package/dist/core/create/interval.mjs +29 -4
- package/dist/core/create/interval.mjs.map +1 -1
- package/dist/core/create/of.d.mts +22 -3
- package/dist/core/create/of.d.mts.map +1 -1
- package/dist/core/create/of.mjs +22 -3
- package/dist/core/create/of.mjs.map +1 -1
- package/dist/core/create/source.d.mts +20 -1
- package/dist/core/create/source.d.mts.map +1 -1
- package/dist/core/create/source.mjs.map +1 -1
- package/dist/core/create/timer.d.mts +23 -4
- package/dist/core/create/timer.d.mts.map +1 -1
- package/dist/core/create/timer.mjs +23 -4
- package/dist/core/create/timer.mjs.map +1 -1
- package/dist/core/operators/audit-time.d.mts +59 -0
- package/dist/core/operators/audit-time.d.mts.map +1 -1
- package/dist/core/operators/audit-time.mjs +59 -0
- package/dist/core/operators/audit-time.mjs.map +1 -1
- package/dist/core/operators/debounce-time.d.mts +22 -2
- package/dist/core/operators/debounce-time.d.mts.map +1 -1
- package/dist/core/operators/debounce-time.mjs +22 -2
- package/dist/core/operators/debounce-time.mjs.map +1 -1
- package/dist/core/operators/filter.d.mts +26 -1
- package/dist/core/operators/filter.d.mts.map +1 -1
- package/dist/core/operators/filter.mjs.map +1 -1
- package/dist/core/operators/map-with-index.d.mts +19 -4
- package/dist/core/operators/map-with-index.d.mts.map +1 -1
- package/dist/core/operators/map-with-index.mjs +19 -4
- package/dist/core/operators/map-with-index.mjs.map +1 -1
- package/dist/core/operators/merge-map.d.mts +47 -5
- package/dist/core/operators/merge-map.d.mts.map +1 -1
- package/dist/core/operators/merge-map.mjs +47 -5
- package/dist/core/operators/merge-map.mjs.map +1 -1
- package/dist/core/operators/pairwise.d.mts +30 -1
- package/dist/core/operators/pairwise.d.mts.map +1 -1
- package/dist/core/operators/pairwise.mjs +30 -1
- package/dist/core/operators/pairwise.mjs.map +1 -1
- package/dist/core/operators/scan.d.mts +23 -1
- package/dist/core/operators/scan.d.mts.map +1 -1
- package/dist/core/operators/scan.mjs +23 -1
- package/dist/core/operators/scan.mjs.map +1 -1
- package/dist/core/operators/skip-if-no-change.d.mts +25 -1
- package/dist/core/operators/skip-if-no-change.d.mts.map +1 -1
- package/dist/core/operators/skip-if-no-change.mjs +25 -1
- package/dist/core/operators/skip-if-no-change.mjs.map +1 -1
- package/dist/core/operators/skip-until.d.mts +50 -0
- package/dist/core/operators/skip-until.d.mts.map +1 -1
- package/dist/core/operators/skip-until.mjs +50 -0
- package/dist/core/operators/skip-until.mjs.map +1 -1
- package/dist/core/operators/skip-while.d.mts +48 -0
- package/dist/core/operators/skip-while.d.mts.map +1 -1
- package/dist/core/operators/skip-while.mjs +48 -0
- package/dist/core/operators/skip-while.mjs.map +1 -1
- package/dist/core/operators/switch-map.d.mts +39 -5
- package/dist/core/operators/switch-map.d.mts.map +1 -1
- package/dist/core/operators/switch-map.mjs +39 -5
- package/dist/core/operators/switch-map.mjs.map +1 -1
- package/dist/core/operators/take-until.d.mts +20 -1
- package/dist/core/operators/take-until.d.mts.map +1 -1
- package/dist/core/operators/take-until.mjs +20 -1
- package/dist/core/operators/take-until.mjs.map +1 -1
- package/dist/core/operators/take-while.d.mts +47 -0
- package/dist/core/operators/take-while.d.mts.map +1 -1
- package/dist/core/operators/take-while.mjs +47 -0
- package/dist/core/operators/take-while.mjs.map +1 -1
- package/dist/core/operators/throttle-time.d.mts +44 -5
- package/dist/core/operators/throttle-time.d.mts.map +1 -1
- package/dist/core/operators/throttle-time.mjs +44 -5
- package/dist/core/operators/throttle-time.mjs.map +1 -1
- package/dist/core/operators/with-buffered-from.d.mts +53 -0
- package/dist/core/operators/with-buffered-from.d.mts.map +1 -1
- package/dist/core/operators/with-buffered-from.mjs +53 -0
- package/dist/core/operators/with-buffered-from.mjs.map +1 -1
- package/dist/core/operators/with-current-value-from.d.mts +55 -0
- package/dist/core/operators/with-current-value-from.d.mts.map +1 -1
- package/dist/core/operators/with-current-value-from.mjs +55 -0
- package/dist/core/operators/with-current-value-from.mjs.map +1 -1
- package/dist/core/operators/with-initial-value.d.mts +24 -2
- package/dist/core/operators/with-initial-value.d.mts.map +1 -1
- package/dist/core/operators/with-initial-value.mjs +24 -2
- package/dist/core/operators/with-initial-value.mjs.map +1 -1
- package/dist/core/types/observable-family.d.mts +7 -7
- package/dist/utils/create-event-emitter.d.mts +20 -2
- package/dist/utils/create-event-emitter.d.mts.map +1 -1
- package/dist/utils/create-event-emitter.mjs +20 -2
- package/dist/utils/create-event-emitter.mjs.map +1 -1
- package/dist/utils/create-reducer.d.mts +13 -1
- package/dist/utils/create-reducer.d.mts.map +1 -1
- package/dist/utils/create-reducer.mjs +13 -1
- package/dist/utils/create-reducer.mjs.map +1 -1
- package/dist/utils/create-state.d.mts +24 -4
- package/dist/utils/create-state.d.mts.map +1 -1
- package/dist/utils/create-state.mjs +24 -4
- package/dist/utils/create-state.mjs.map +1 -1
- package/package.json +13 -12
- package/src/core/combine/combine.mts +32 -2
- package/src/core/combine/merge.mts +30 -4
- package/src/core/combine/zip.mts +28 -3
- package/src/core/create/from-array.mts +21 -3
- package/src/core/create/from-promise.mts +29 -7
- package/src/core/create/from-subscribable.mts +58 -0
- package/src/core/create/interval.mts +29 -4
- package/src/core/create/of.mts +22 -3
- package/src/core/create/source.mts +20 -1
- package/src/core/create/timer.mts +23 -4
- package/src/core/operators/audit-time.mts +59 -0
- package/src/core/operators/debounce-time.mts +22 -2
- package/src/core/operators/filter.mts +26 -1
- package/src/core/operators/map-with-index.mts +19 -4
- package/src/core/operators/merge-map.mts +47 -5
- package/src/core/operators/pairwise.mts +30 -1
- package/src/core/operators/scan.mts +23 -1
- package/src/core/operators/skip-if-no-change.mts +25 -1
- package/src/core/operators/skip-until.mts +50 -0
- package/src/core/operators/skip-while.mts +48 -0
- package/src/core/operators/switch-map.mts +39 -5
- package/src/core/operators/take-until.mts +20 -1
- package/src/core/operators/take-while.mts +47 -0
- package/src/core/operators/throttle-time.mts +44 -5
- package/src/core/operators/with-buffered-from.mts +53 -0
- package/src/core/operators/with-current-value-from.mts +55 -0
- package/src/core/operators/with-initial-value.mts +24 -2
- package/src/core/types/observable-family.mts +7 -7
- package/src/utils/create-event-emitter.mts +20 -2
- package/src/utils/create-reducer.mts +13 -1
- package/src/utils/create-state.mts +24 -4
|
@@ -27,17 +27,42 @@ import { withInitialValue } from './with-initial-value.mjs';
|
|
|
27
27
|
*
|
|
28
28
|
* @example
|
|
29
29
|
* ```ts
|
|
30
|
+
* // Timeline:
|
|
31
|
+
* //
|
|
32
|
+
* // num$ 1 2 3 4 5 6
|
|
33
|
+
* // even$ 2 4 6
|
|
34
|
+
* //
|
|
35
|
+
* // Explanation:
|
|
36
|
+
* // - filter passes through only values that satisfy the predicate
|
|
37
|
+
* // - Only even numbers (2, 4, 6) are emitted
|
|
38
|
+
*
|
|
30
39
|
* const num$ = source<number>();
|
|
31
40
|
*
|
|
32
41
|
* const even$ = num$.pipe(filter((x) => x % 2 === 0));
|
|
33
42
|
*
|
|
43
|
+
* const mut_history: number[] = [];
|
|
44
|
+
*
|
|
34
45
|
* even$.subscribe((x) => {
|
|
35
|
-
*
|
|
46
|
+
* mut_history.push(x);
|
|
36
47
|
* });
|
|
37
48
|
*
|
|
38
49
|
* num$.next(1); // nothing logged
|
|
39
50
|
*
|
|
40
51
|
* num$.next(2); // logs: 2
|
|
52
|
+
*
|
|
53
|
+
* assert.deepStrictEqual(mut_history, [2]);
|
|
54
|
+
*
|
|
55
|
+
* num$.next(3); // nothing logged
|
|
56
|
+
*
|
|
57
|
+
* num$.next(4); // logs: 4
|
|
58
|
+
*
|
|
59
|
+
* assert.deepStrictEqual(mut_history, [2, 4]);
|
|
60
|
+
*
|
|
61
|
+
* num$.next(5);
|
|
62
|
+
*
|
|
63
|
+
* num$.next(6);
|
|
64
|
+
*
|
|
65
|
+
* assert.deepStrictEqual(mut_history, [2, 4, 6]);
|
|
41
66
|
* ```
|
|
42
67
|
*/
|
|
43
68
|
export function filter<A, B extends A>(
|
|
@@ -26,17 +26,32 @@ import { withInitialValue } from './with-initial-value.mjs';
|
|
|
26
26
|
*
|
|
27
27
|
* @example
|
|
28
28
|
* ```ts
|
|
29
|
-
*
|
|
29
|
+
* // Timeline:
|
|
30
|
+
* //
|
|
31
|
+
* // num$ "a" "b" "c"
|
|
32
|
+
* // indexed$ "0: a" "1: b" "2: c"
|
|
33
|
+
* //
|
|
34
|
+
* // Explanation:
|
|
35
|
+
* // - mapWithIndex transforms each value along with its index
|
|
36
|
+
* // - Index starts at 0 and increments with each emission
|
|
37
|
+
*
|
|
38
|
+
* const num$ = source<string>();
|
|
30
39
|
*
|
|
31
40
|
* const indexed$ = num$.pipe(mapWithIndex((x, i) => `${i}: ${x}`));
|
|
32
41
|
*
|
|
42
|
+
* const mut_history: string[] = [];
|
|
43
|
+
*
|
|
33
44
|
* indexed$.subscribe((s) => {
|
|
34
|
-
*
|
|
45
|
+
* mut_history.push(s);
|
|
35
46
|
* });
|
|
36
47
|
*
|
|
37
|
-
* num$.next(
|
|
48
|
+
* num$.next('a'); // 0: a
|
|
49
|
+
*
|
|
50
|
+
* num$.next('b'); // 1: b
|
|
51
|
+
*
|
|
52
|
+
* num$.next('c'); // 2: c
|
|
38
53
|
*
|
|
39
|
-
*
|
|
54
|
+
* assert.deepStrictEqual(mut_history, ['0: a', '1: b', '2: c']);
|
|
40
55
|
* ```
|
|
41
56
|
*/
|
|
42
57
|
export const mapWithIndex = <A, B>(
|
|
@@ -19,16 +19,58 @@ import {
|
|
|
19
19
|
*
|
|
20
20
|
* @example
|
|
21
21
|
* ```ts
|
|
22
|
+
* // Timeline:
|
|
23
|
+
* //
|
|
24
|
+
* // ids$ 1 2 3
|
|
25
|
+
* // requests fetch(1) fetch(2) fetch(3)
|
|
26
|
+
* // users$ result1 result2 result3
|
|
27
|
+
* // (parallel) (parallel) (parallel)
|
|
28
|
+
* //
|
|
29
|
+
* // Explanation:
|
|
30
|
+
* // - mergeMap runs all inner observables in parallel
|
|
31
|
+
* // - Results are emitted as they arrive (may be out of order)
|
|
32
|
+
* // - Does NOT cancel previous requests
|
|
33
|
+
* // - All requests run concurrently and all results are emitted
|
|
34
|
+
*
|
|
22
35
|
* const ids$ = source<number>();
|
|
23
36
|
*
|
|
24
|
-
* const users$ = ids$.pipe(
|
|
37
|
+
* const users$ = ids$.pipe(
|
|
38
|
+
* mergeMap((id) => {
|
|
39
|
+
* const result$ = source<{ id: number }>();
|
|
40
|
+
*
|
|
41
|
+
* setTimeout(() => {
|
|
42
|
+
* result$.next({ id });
|
|
43
|
+
*
|
|
44
|
+
* result$.complete();
|
|
45
|
+
* }, 10);
|
|
46
|
+
*
|
|
47
|
+
* return result$;
|
|
48
|
+
* }),
|
|
49
|
+
* );
|
|
25
50
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
51
|
+
* const mut_history: { id: number }[] = [];
|
|
52
|
+
*
|
|
53
|
+
* users$.subscribe((value) => {
|
|
54
|
+
* mut_history.push(value);
|
|
28
55
|
* });
|
|
29
|
-
* // All requests run in parallel, results merged as they arrive
|
|
30
56
|
*
|
|
31
|
-
*
|
|
57
|
+
* ids$.next(1);
|
|
58
|
+
*
|
|
59
|
+
* ids$.next(2);
|
|
60
|
+
*
|
|
61
|
+
* ids$.next(3);
|
|
62
|
+
*
|
|
63
|
+
* await new Promise((resolve) => {
|
|
64
|
+
* setTimeout(resolve, 200);
|
|
65
|
+
* });
|
|
66
|
+
*
|
|
67
|
+
* assert.deepStrictEqual(mut_history.length, 3);
|
|
68
|
+
*
|
|
69
|
+
* assert.isTrue(mut_history.some((u) => u.id === 1));
|
|
70
|
+
*
|
|
71
|
+
* assert.isTrue(mut_history.some((u) => u.id === 2));
|
|
72
|
+
*
|
|
73
|
+
* assert.isTrue(mut_history.some((u) => u.id === 3));
|
|
32
74
|
* ```
|
|
33
75
|
*
|
|
34
76
|
* @note To improve code readability, consider using `createState` instead of `mergeMap`,
|
|
@@ -16,19 +16,48 @@ import {
|
|
|
16
16
|
*
|
|
17
17
|
* @example
|
|
18
18
|
* ```ts
|
|
19
|
+
* // Timeline:
|
|
20
|
+
* //
|
|
21
|
+
* // num$ 1 2 3 4
|
|
22
|
+
* // pairs$ [1,2] [2,3] [3,4]
|
|
23
|
+
* //
|
|
24
|
+
* // Explanation:
|
|
25
|
+
* // - pairwise emits the current and previous values as a tuple
|
|
26
|
+
* // - Nothing is emitted for the first value (no previous value yet)
|
|
27
|
+
* // - Useful for tracking changes between consecutive values
|
|
28
|
+
*
|
|
19
29
|
* const num$ = source<number>();
|
|
20
30
|
*
|
|
21
31
|
* const pairs$ = num$.pipe(pairwise());
|
|
22
32
|
*
|
|
33
|
+
* const mut_history: (readonly [number, number])[] = [];
|
|
34
|
+
*
|
|
23
35
|
* pairs$.subscribe(([prev, curr]) => {
|
|
24
|
-
*
|
|
36
|
+
* mut_history.push([prev, curr]);
|
|
25
37
|
* });
|
|
26
38
|
*
|
|
27
39
|
* num$.next(1); // nothing logged
|
|
28
40
|
*
|
|
41
|
+
* assert.deepStrictEqual(mut_history, []);
|
|
42
|
+
*
|
|
29
43
|
* num$.next(2); // logs: 1, 2
|
|
30
44
|
*
|
|
45
|
+
* assert.deepStrictEqual(mut_history, [[1, 2]]);
|
|
46
|
+
*
|
|
31
47
|
* num$.next(3); // logs: 2, 3
|
|
48
|
+
*
|
|
49
|
+
* assert.deepStrictEqual(mut_history, [
|
|
50
|
+
* [1, 2],
|
|
51
|
+
* [2, 3],
|
|
52
|
+
* ]);
|
|
53
|
+
*
|
|
54
|
+
* num$.next(4); // logs: 3, 4
|
|
55
|
+
*
|
|
56
|
+
* assert.deepStrictEqual(mut_history, [
|
|
57
|
+
* [1, 2],
|
|
58
|
+
* [2, 3],
|
|
59
|
+
* [3, 4],
|
|
60
|
+
* ]);
|
|
32
61
|
* ```
|
|
33
62
|
*/
|
|
34
63
|
export const pairwise = <A,>(): DropInitialValueOperator<A, readonly [A, A]> =>
|
|
@@ -19,19 +19,41 @@ import {
|
|
|
19
19
|
*
|
|
20
20
|
* @example
|
|
21
21
|
* ```ts
|
|
22
|
+
* // Timeline (accumulating sum):
|
|
23
|
+
* //
|
|
24
|
+
* // num$ 1 2 3 4 5
|
|
25
|
+
* // sum$ 1 3 6 10 15
|
|
26
|
+
* // | | | | |
|
|
27
|
+
* // 0+1 1+2 3+3 6+4 10+5
|
|
28
|
+
* //
|
|
29
|
+
* // Explanation:
|
|
30
|
+
* // - scan accumulates values over time using a reducer function
|
|
31
|
+
* // - Starting with seed value 0, each emission adds to the accumulator
|
|
32
|
+
* // - Similar to Array.reduce, but for streams
|
|
33
|
+
*
|
|
22
34
|
* const num$ = source<number>();
|
|
23
35
|
*
|
|
24
36
|
* const sum$ = num$.pipe(scan((acc, curr) => acc + curr, 0));
|
|
25
37
|
*
|
|
38
|
+
* const mut_history: number[] = [];
|
|
39
|
+
*
|
|
26
40
|
* sum$.subscribe((x) => {
|
|
27
|
-
*
|
|
41
|
+
* mut_history.push(x);
|
|
28
42
|
* });
|
|
29
43
|
*
|
|
44
|
+
* assert.deepStrictEqual(mut_history, [0]);
|
|
45
|
+
*
|
|
30
46
|
* num$.next(1); // logs: 1
|
|
31
47
|
*
|
|
48
|
+
* assert.deepStrictEqual(mut_history, [0, 1]);
|
|
49
|
+
*
|
|
32
50
|
* num$.next(2); // logs: 3
|
|
33
51
|
*
|
|
52
|
+
* assert.deepStrictEqual(mut_history, [0, 1, 3]);
|
|
53
|
+
*
|
|
34
54
|
* num$.next(3); // logs: 6
|
|
55
|
+
*
|
|
56
|
+
* assert.deepStrictEqual(mut_history, [0, 1, 3, 6]);
|
|
35
57
|
* ```
|
|
36
58
|
*/
|
|
37
59
|
export const scan =
|
|
@@ -17,19 +17,43 @@ import {
|
|
|
17
17
|
*
|
|
18
18
|
* @example
|
|
19
19
|
* ```ts
|
|
20
|
+
* // Timeline:
|
|
21
|
+
* //
|
|
22
|
+
* // num$ 1 1 2 2 2 3
|
|
23
|
+
* // distinct$ 1 2 3
|
|
24
|
+
* //
|
|
25
|
+
* // Explanation:
|
|
26
|
+
* // - skipIfNoChange filters out consecutive duplicate values
|
|
27
|
+
* // - Uses strict equality (===) for comparison
|
|
28
|
+
* // - Only emits when the value actually changes
|
|
29
|
+
*
|
|
20
30
|
* const num$ = source<number>();
|
|
21
31
|
*
|
|
22
32
|
* const distinct$ = num$.pipe(skipIfNoChange());
|
|
23
33
|
*
|
|
34
|
+
* const mut_history: number[] = [];
|
|
35
|
+
*
|
|
24
36
|
* distinct$.subscribe((x) => {
|
|
25
|
-
*
|
|
37
|
+
* mut_history.push(x);
|
|
26
38
|
* });
|
|
27
39
|
*
|
|
28
40
|
* num$.next(1); // logs: 1
|
|
29
41
|
*
|
|
42
|
+
* assert.deepStrictEqual(mut_history, [1]);
|
|
43
|
+
*
|
|
30
44
|
* num$.next(1); // nothing logged
|
|
31
45
|
*
|
|
46
|
+
* assert.deepStrictEqual(mut_history, [1]);
|
|
47
|
+
*
|
|
32
48
|
* num$.next(2); // logs: 2
|
|
49
|
+
*
|
|
50
|
+
* assert.deepStrictEqual(mut_history, [1, 2]);
|
|
51
|
+
*
|
|
52
|
+
* num$.next(2); // nothing logged
|
|
53
|
+
*
|
|
54
|
+
* num$.next(3); // logs: 3
|
|
55
|
+
*
|
|
56
|
+
* assert.deepStrictEqual(mut_history, [1, 2, 3]);
|
|
33
57
|
* ```
|
|
34
58
|
*/
|
|
35
59
|
export const skipIfNoChange = <A,>(
|
|
@@ -7,6 +7,56 @@ import {
|
|
|
7
7
|
type UpdaterSymbol,
|
|
8
8
|
} from '../types/index.mjs';
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Skips all values from the source observable until the notifier observable emits.
|
|
12
|
+
*
|
|
13
|
+
* @template A - The type of values from the source
|
|
14
|
+
* @param notifier - An observable that signals when to start emitting
|
|
15
|
+
* @returns An operator that skips values until the notifier emits
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* // Timeline:
|
|
20
|
+
* //
|
|
21
|
+
* // num$ 1 2 3 start 4 5 6
|
|
22
|
+
* // startNotifier X
|
|
23
|
+
* // skipped$ 4 5 6
|
|
24
|
+
* // |------ skipped -------|
|
|
25
|
+
* //
|
|
26
|
+
* // Explanation:
|
|
27
|
+
* // - skipUntil ignores all values until the notifier emits
|
|
28
|
+
* // - After the notifier emits, all subsequent values are passed through
|
|
29
|
+
* // - Opposite of takeUntil (which completes when notifier emits)
|
|
30
|
+
*
|
|
31
|
+
* const num$ = source<number>();
|
|
32
|
+
*
|
|
33
|
+
* const [startNotifier, start_] = createEventEmitter();
|
|
34
|
+
*
|
|
35
|
+
* const skipped$ = num$.pipe(skipUntil(startNotifier));
|
|
36
|
+
*
|
|
37
|
+
* const mut_history: number[] = [];
|
|
38
|
+
*
|
|
39
|
+
* skipped$.subscribe((x) => {
|
|
40
|
+
* mut_history.push(x);
|
|
41
|
+
* });
|
|
42
|
+
*
|
|
43
|
+
* num$.next(1); // nothing logged
|
|
44
|
+
*
|
|
45
|
+
* num$.next(2); // nothing logged
|
|
46
|
+
*
|
|
47
|
+
* assert.deepStrictEqual(mut_history, []);
|
|
48
|
+
*
|
|
49
|
+
* start_();
|
|
50
|
+
*
|
|
51
|
+
* num$.next(4); // logs: 4
|
|
52
|
+
*
|
|
53
|
+
* assert.deepStrictEqual(mut_history, [4]);
|
|
54
|
+
*
|
|
55
|
+
* num$.next(5); // logs: 5
|
|
56
|
+
*
|
|
57
|
+
* assert.deepStrictEqual(mut_history, [4, 5]);
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
10
60
|
export const skipUntil =
|
|
11
61
|
<A,>(notifier: Observable<unknown>): DropInitialValueOperator<A, A> =>
|
|
12
62
|
(parentObservable) =>
|
|
@@ -13,6 +13,54 @@ import {
|
|
|
13
13
|
type UpdaterSymbol,
|
|
14
14
|
} from '../types/index.mjs';
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Skips values from the source observable while the predicate returns true.
|
|
18
|
+
* Once the predicate returns false, all subsequent values pass through.
|
|
19
|
+
*
|
|
20
|
+
* @template A - The type of values from the source
|
|
21
|
+
* @param predicate - Function to test each value
|
|
22
|
+
* @returns An operator that skips values while the predicate is true
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* // Timeline:
|
|
27
|
+
* //
|
|
28
|
+
* // num$ 1 2 3 4 5 6 7
|
|
29
|
+
* // skipped$ 5 6 7
|
|
30
|
+
* // |---- skip -----|
|
|
31
|
+
* //
|
|
32
|
+
* // Explanation:
|
|
33
|
+
* // - skipWhile skips values while the predicate returns true
|
|
34
|
+
* // - Once the predicate returns false, all subsequent values pass through
|
|
35
|
+
* // - Unlike filter, the predicate is never checked again after the first false
|
|
36
|
+
*
|
|
37
|
+
* const num$ = source<number>();
|
|
38
|
+
*
|
|
39
|
+
* const skipped$ = num$.pipe(skipWhile((x) => x < 5));
|
|
40
|
+
*
|
|
41
|
+
* const mut_history: number[] = [];
|
|
42
|
+
*
|
|
43
|
+
* skipped$.subscribe((x) => {
|
|
44
|
+
* mut_history.push(x);
|
|
45
|
+
* });
|
|
46
|
+
*
|
|
47
|
+
* num$.next(1); // nothing logged
|
|
48
|
+
*
|
|
49
|
+
* num$.next(2); // nothing logged
|
|
50
|
+
*
|
|
51
|
+
* num$.next(5); // logs: 5
|
|
52
|
+
*
|
|
53
|
+
* assert.deepStrictEqual(mut_history, [5]);
|
|
54
|
+
*
|
|
55
|
+
* num$.next(6); // logs: 6
|
|
56
|
+
*
|
|
57
|
+
* assert.deepStrictEqual(mut_history, [5, 6]);
|
|
58
|
+
*
|
|
59
|
+
* num$.next(7); // logs: 7
|
|
60
|
+
*
|
|
61
|
+
* assert.deepStrictEqual(mut_history, [5, 6, 7]);
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
16
64
|
export const skipWhile =
|
|
17
65
|
<A,>(
|
|
18
66
|
predicate: (value: A, index: SafeUint | -1) => boolean,
|
|
@@ -19,18 +19,52 @@ import {
|
|
|
19
19
|
*
|
|
20
20
|
* @example
|
|
21
21
|
* ```ts
|
|
22
|
+
* // Timeline:
|
|
23
|
+
* //
|
|
24
|
+
* // searchQuery$ "a" "ab" "abc"
|
|
25
|
+
* // requests fetch1 fetch2 fetch3
|
|
26
|
+
* // results$ cancel cancel result3
|
|
27
|
+
* // fetch1 fetch2
|
|
28
|
+
* //
|
|
29
|
+
* // Explanation:
|
|
30
|
+
* // - switchMap cancels previous inner observables when a new value arrives
|
|
31
|
+
* // - Only the result from the latest search query is emitted
|
|
32
|
+
* // - Previous ongoing requests are cancelled
|
|
33
|
+
* // - Ideal for search-as-you-type scenarios
|
|
34
|
+
*
|
|
22
35
|
* const searchQuery$ = source<string>();
|
|
23
36
|
*
|
|
24
37
|
* const results$ = searchQuery$.pipe(
|
|
25
|
-
* switchMap((query) =>
|
|
38
|
+
* switchMap((query) => {
|
|
39
|
+
* const result$ = source<string[]>();
|
|
40
|
+
*
|
|
41
|
+
* setTimeout(() => {
|
|
42
|
+
* result$.next([query]);
|
|
43
|
+
*
|
|
44
|
+
* result$.complete();
|
|
45
|
+
* }, 10);
|
|
46
|
+
*
|
|
47
|
+
* return result$;
|
|
48
|
+
* }),
|
|
26
49
|
* );
|
|
27
50
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
51
|
+
* const mut_history: string[][] = [];
|
|
52
|
+
*
|
|
53
|
+
* results$.subscribe((value) => {
|
|
54
|
+
* mut_history.push(value);
|
|
55
|
+
* });
|
|
56
|
+
*
|
|
57
|
+
* searchQuery$.next('a');
|
|
58
|
+
*
|
|
59
|
+
* searchQuery$.next('ab');
|
|
60
|
+
*
|
|
61
|
+
* searchQuery$.next('abc');
|
|
62
|
+
*
|
|
63
|
+
* await new Promise((resolve) => {
|
|
64
|
+
* setTimeout(resolve, 200);
|
|
30
65
|
* });
|
|
31
|
-
* // Only the latest search results are emitted, previous searches are cancelled
|
|
32
66
|
*
|
|
33
|
-
*
|
|
67
|
+
* assert.deepStrictEqual(mut_history, [['abc']]);
|
|
34
68
|
* ```
|
|
35
69
|
*
|
|
36
70
|
* @note To improve code readability, consider using `createState` instead of `switchMap`,
|
|
@@ -17,23 +17,42 @@ import {
|
|
|
17
17
|
*
|
|
18
18
|
* @example
|
|
19
19
|
* ```ts
|
|
20
|
+
* // Timeline:
|
|
21
|
+
* //
|
|
22
|
+
* // num$ 1 2 stop 3 (ignored)
|
|
23
|
+
* // stopNotifier X
|
|
24
|
+
* // limited$ 1 2 |------- (completed)
|
|
25
|
+
* //
|
|
26
|
+
* // Explanation:
|
|
27
|
+
* // - takeUntil completes the observable when the notifier emits
|
|
28
|
+
* // - After stop() is called, no further values are emitted
|
|
29
|
+
* // - Useful for cleanup and cancellation patterns
|
|
30
|
+
*
|
|
20
31
|
* const num$ = source<number>();
|
|
21
32
|
*
|
|
22
33
|
* const [stopNotifier, stop_] = createEventEmitter();
|
|
23
34
|
*
|
|
24
35
|
* const limited$ = num$.pipe(takeUntil(stopNotifier));
|
|
25
36
|
*
|
|
37
|
+
* const mut_history: number[] = [];
|
|
38
|
+
*
|
|
26
39
|
* limited$.subscribe((x) => {
|
|
27
|
-
*
|
|
40
|
+
* mut_history.push(x);
|
|
28
41
|
* });
|
|
29
42
|
*
|
|
30
43
|
* num$.next(1); // logs: 1
|
|
31
44
|
*
|
|
45
|
+
* assert.deepStrictEqual(mut_history, [1]);
|
|
46
|
+
*
|
|
32
47
|
* num$.next(2); // logs: 2
|
|
33
48
|
*
|
|
49
|
+
* assert.deepStrictEqual(mut_history, [1, 2]);
|
|
50
|
+
*
|
|
34
51
|
* stop_();
|
|
35
52
|
*
|
|
36
53
|
* num$.next(3); // nothing logged (completed)
|
|
54
|
+
*
|
|
55
|
+
* assert.deepStrictEqual(mut_history, [1, 2]);
|
|
37
56
|
* ```
|
|
38
57
|
*/
|
|
39
58
|
export const takeUntil = <A,>(
|
|
@@ -16,6 +16,53 @@ import {
|
|
|
16
16
|
} from '../types/index.mjs';
|
|
17
17
|
import { withInitialValue } from './with-initial-value.mjs';
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Emits values from the source observable while the predicate returns true.
|
|
21
|
+
* Completes immediately when the predicate returns false.
|
|
22
|
+
*
|
|
23
|
+
* @template A - The type of values from the source
|
|
24
|
+
* @param predicate - Function to test each value
|
|
25
|
+
* @returns An operator that takes values while the predicate is true
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* // Timeline:
|
|
30
|
+
* //
|
|
31
|
+
* // num$ 1 2 3 4 5 6 (ignored)
|
|
32
|
+
* // taken$ 1 2 3 4 | (completes)
|
|
33
|
+
* //
|
|
34
|
+
* // Explanation:
|
|
35
|
+
* // - takeWhile emits values while the predicate returns true
|
|
36
|
+
* // - Completes immediately when the predicate returns false
|
|
37
|
+
* // - No further values are emitted after completion
|
|
38
|
+
*
|
|
39
|
+
* const num$ = source<number>();
|
|
40
|
+
*
|
|
41
|
+
* const taken$ = num$.pipe(takeWhile((x) => x < 5));
|
|
42
|
+
*
|
|
43
|
+
* const mut_history: number[] = [];
|
|
44
|
+
*
|
|
45
|
+
* taken$.subscribe((x) => {
|
|
46
|
+
* mut_history.push(x);
|
|
47
|
+
* });
|
|
48
|
+
*
|
|
49
|
+
* num$.next(1); // logs: 1
|
|
50
|
+
*
|
|
51
|
+
* assert.deepStrictEqual(mut_history, [1]);
|
|
52
|
+
*
|
|
53
|
+
* num$.next(2); // logs: 2
|
|
54
|
+
*
|
|
55
|
+
* assert.deepStrictEqual(mut_history, [1, 2]);
|
|
56
|
+
*
|
|
57
|
+
* num$.next(5); // nothing logged (completes)
|
|
58
|
+
*
|
|
59
|
+
* assert.deepStrictEqual(mut_history, [1, 2]);
|
|
60
|
+
*
|
|
61
|
+
* num$.next(6); // nothing logged (already completed)
|
|
62
|
+
*
|
|
63
|
+
* assert.deepStrictEqual(mut_history, [1, 2]);
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
19
66
|
export const takeWhile =
|
|
20
67
|
<A,>(
|
|
21
68
|
predicate: (value: A, index: SafeUint | -1) => boolean,
|
|
@@ -17,14 +17,53 @@ import {
|
|
|
17
17
|
*
|
|
18
18
|
* @example
|
|
19
19
|
* ```ts
|
|
20
|
-
*
|
|
20
|
+
* // Timeline (1000ms throttle):
|
|
21
|
+
* //
|
|
22
|
+
* // Time(ms) 0 100 200 300 ... 1000 1100 1200 ... 2000 2100
|
|
23
|
+
* // scroll$ e1 e2 e3 e4 e5 e6 e7 e8 e9
|
|
24
|
+
* // throttled$ e1 e5 e8
|
|
25
|
+
* // |-------1000ms------> |------1000ms------> |------1000ms------>
|
|
26
|
+
* //
|
|
27
|
+
* // Explanation:
|
|
28
|
+
* // - throttleTime emits the first value immediately, then ignores subsequent values
|
|
29
|
+
* // for the specified duration (1000ms)
|
|
30
|
+
* // - At 0ms: e1 is emitted immediately
|
|
31
|
+
* // - At 100-300ms: e2, e3, e4 are ignored (within 1000ms window)
|
|
32
|
+
* // - At 1000ms: e5 is emitted (1000ms has passed since e1)
|
|
33
|
+
* // - At 1100-1200ms: e6, e7 are ignored
|
|
34
|
+
* // - At 2000ms: e8 is emitted (1000ms has passed since e5)
|
|
21
35
|
*
|
|
22
|
-
* const
|
|
36
|
+
* const scroll$ = source<number>();
|
|
23
37
|
*
|
|
24
|
-
* throttled$.
|
|
25
|
-
*
|
|
38
|
+
* const throttled$ = scroll$.pipe(throttleTime(200));
|
|
39
|
+
*
|
|
40
|
+
* const mut_history: number[] = [];
|
|
41
|
+
*
|
|
42
|
+
* throttled$.subscribe((value) => {
|
|
43
|
+
* mut_history.push(value);
|
|
26
44
|
* });
|
|
27
|
-
*
|
|
45
|
+
*
|
|
46
|
+
* scroll$.next(1);
|
|
47
|
+
*
|
|
48
|
+
* assert.deepStrictEqual(mut_history, [1]);
|
|
49
|
+
*
|
|
50
|
+
* await new Promise((resolve) => {
|
|
51
|
+
* setTimeout(resolve, 50);
|
|
52
|
+
* });
|
|
53
|
+
*
|
|
54
|
+
* scroll$.next(2);
|
|
55
|
+
*
|
|
56
|
+
* scroll$.next(3);
|
|
57
|
+
*
|
|
58
|
+
* assert.deepStrictEqual(mut_history, [1]);
|
|
59
|
+
*
|
|
60
|
+
* await new Promise((resolve) => {
|
|
61
|
+
* setTimeout(resolve, 200);
|
|
62
|
+
* });
|
|
63
|
+
*
|
|
64
|
+
* scroll$.next(4);
|
|
65
|
+
*
|
|
66
|
+
* assert.deepStrictEqual(mut_history, [1, 4]);
|
|
28
67
|
* ```
|
|
29
68
|
*/
|
|
30
69
|
export const throttleTime = <A,>(
|
|
@@ -8,6 +8,59 @@ import {
|
|
|
8
8
|
} from '../types/index.mjs';
|
|
9
9
|
import { maxDepth } from '../utils/index.mjs';
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Buffers values from the source observable and emits them along with the parent value
|
|
13
|
+
* when the parent emits. The buffer is cleared after each emission.
|
|
14
|
+
*
|
|
15
|
+
* @template A - The type of values from the parent observable
|
|
16
|
+
* @template B - The type of values from the source observable
|
|
17
|
+
* @param observable - The observable whose values will be buffered
|
|
18
|
+
* @returns An operator that emits tuples of [parentValue, bufferedValues]
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* // Timeline:
|
|
23
|
+
* //
|
|
24
|
+
* // data$ d1 d2 d3 d4 d5 d6 d7 d8
|
|
25
|
+
* // trigger$ T1 T2 T3
|
|
26
|
+
* // result$ [T1,[d1,d2,d3]] [T2,[d4,d5,d6]] [T3,[d7,d8]]
|
|
27
|
+
* //
|
|
28
|
+
* // Explanation:
|
|
29
|
+
* // - withBufferedFrom collects values from the source observable
|
|
30
|
+
* // - When the trigger emits, it emits a tuple of [triggerValue, bufferedValues]
|
|
31
|
+
* // - Buffer is cleared after each emission
|
|
32
|
+
* // - Useful for batching data collection triggered by events
|
|
33
|
+
*
|
|
34
|
+
* const data$ = source<string>();
|
|
35
|
+
*
|
|
36
|
+
* const trigger$ = source<number>();
|
|
37
|
+
*
|
|
38
|
+
* const result$ = trigger$.pipe(withBufferedFrom(data$));
|
|
39
|
+
*
|
|
40
|
+
* const mut_history: (readonly [number, readonly string[]])[] = [];
|
|
41
|
+
*
|
|
42
|
+
* result$.subscribe(([triggerValue, bufferedData]) => {
|
|
43
|
+
* mut_history.push([triggerValue, bufferedData]);
|
|
44
|
+
* });
|
|
45
|
+
*
|
|
46
|
+
* data$.next('a');
|
|
47
|
+
*
|
|
48
|
+
* data$.next('b');
|
|
49
|
+
*
|
|
50
|
+
* trigger$.next(1);
|
|
51
|
+
*
|
|
52
|
+
* assert.deepStrictEqual(mut_history, [[1, ['a', 'b']]]);
|
|
53
|
+
*
|
|
54
|
+
* data$.next('c');
|
|
55
|
+
*
|
|
56
|
+
* trigger$.next(2);
|
|
57
|
+
*
|
|
58
|
+
* assert.deepStrictEqual(mut_history, [
|
|
59
|
+
* [1, ['a', 'b']],
|
|
60
|
+
* [2, ['c']],
|
|
61
|
+
* ]);
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
11
64
|
export const withBufferedFrom = <A, B>(
|
|
12
65
|
observable: Observable<B>,
|
|
13
66
|
): KeepInitialValueOperator<A, readonly [A, readonly B[]]> =>
|