synstate 3.3.0 → 3.4.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 +9 -2
- package/dist/core/base/create-child-observable.d.mts.map +1 -1
- package/dist/core/base/create-child-observable.mjs +26 -8
- package/dist/core/base/create-child-observable.mjs.map +1 -1
- package/dist/core/base/create-observable-base.d.mts +2 -0
- package/dist/core/base/create-observable-base.d.mts.map +1 -1
- package/dist/core/base/create-observable-base.mjs +21 -3
- package/dist/core/base/create-observable-base.mjs.map +1 -1
- package/dist/core/base/create-root-observable.d.mts +9 -0
- package/dist/core/base/create-root-observable.d.mts.map +1 -1
- package/dist/core/base/create-root-observable.mjs +25 -2
- package/dist/core/base/create-root-observable.mjs.map +1 -1
- package/dist/core/combine/merge.d.mts +0 -3
- package/dist/core/combine/merge.d.mts.map +1 -1
- package/dist/core/combine/merge.mjs +0 -3
- package/dist/core/combine/merge.mjs.map +1 -1
- package/dist/core/combine/zip.d.mts +4 -2
- package/dist/core/combine/zip.d.mts.map +1 -1
- package/dist/core/combine/zip.mjs +21 -3
- package/dist/core/combine/zip.mjs.map +1 -1
- package/dist/core/create/counter.d.mts +9 -3
- package/dist/core/create/counter.d.mts.map +1 -1
- package/dist/core/create/counter.mjs +16 -3
- package/dist/core/create/counter.mjs.map +1 -1
- package/dist/core/create/from-subscribable.d.mts.map +1 -1
- package/dist/core/create/from-subscribable.mjs +34 -11
- package/dist/core/create/from-subscribable.mjs.map +1 -1
- package/dist/core/create/source.d.mts +7 -0
- package/dist/core/create/source.d.mts.map +1 -1
- package/dist/core/create/source.mjs +6 -2
- package/dist/core/create/source.mjs.map +1 -1
- package/dist/core/operators/merge-map.d.mts +0 -3
- package/dist/core/operators/merge-map.d.mts.map +1 -1
- package/dist/core/operators/merge-map.mjs +0 -3
- package/dist/core/operators/merge-map.mjs.map +1 -1
- package/dist/core/operators/skip-until.d.mts.map +1 -1
- package/dist/core/operators/skip-until.mjs +15 -6
- package/dist/core/operators/skip-until.mjs.map +1 -1
- package/dist/core/operators/switch-map.d.mts +0 -3
- package/dist/core/operators/switch-map.d.mts.map +1 -1
- package/dist/core/operators/switch-map.mjs +0 -3
- 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 +13 -1
- package/dist/core/operators/take-until.mjs.map +1 -1
- package/dist/core/operators/with-buffered-from.d.mts.map +1 -1
- package/dist/core/operators/with-buffered-from.mjs +12 -3
- package/dist/core/operators/with-buffered-from.mjs.map +1 -1
- package/dist/core/types/observable-family.d.mts +5 -1
- package/dist/core/types/observable-family.d.mts.map +1 -1
- package/dist/core/types/observable-family.mjs.map +1 -1
- package/dist/utils/create-event-emitter.d.mts +2 -2
- package/dist/utils/create-reducer.d.mts +10 -1
- package/dist/utils/create-reducer.d.mts.map +1 -1
- package/dist/utils/create-reducer.mjs +13 -5
- package/dist/utils/create-reducer.mjs.map +1 -1
- package/dist/utils/create-state.d.mts +7 -0
- package/dist/utils/create-state.d.mts.map +1 -1
- package/dist/utils/create-state.mjs +7 -0
- package/dist/utils/create-state.mjs.map +1 -1
- package/package.json +4 -4
- package/src/core/base/create-child-observable.mts +34 -6
- package/src/core/base/create-observable-base.mts +23 -3
- package/src/core/base/create-root-observable.mts +35 -1
- package/src/core/base/write-during-propagation.test.mts +234 -0
- package/src/core/combine/merge.mts +0 -3
- package/src/core/combine/zip.mts +36 -5
- package/src/core/create/counter.mts +27 -6
- package/src/core/create/counter.test.mts +82 -0
- package/src/core/create/from-subscribable.mts +33 -5
- package/src/core/create/from-subscribable.test.mts +129 -0
- package/src/core/create/source.mts +18 -3
- package/src/core/create/source.test.mts +41 -0
- package/src/core/operators/merge-map.mts +0 -3
- package/src/core/operators/skip-until.mts +21 -9
- package/src/core/operators/switch-map.mts +0 -3
- package/src/core/operators/take-until-skip-until-teardown.test.mts +127 -0
- package/src/core/operators/take-until.mts +15 -1
- package/src/core/operators/with-buffered-from.mts +15 -3
- package/src/core/operators/with-buffered-from.test.mts +56 -0
- package/src/core/types/observable-family.mts +5 -1
- package/src/utils/create-event-emitter.mts +2 -2
- package/src/utils/create-reducer.mts +14 -8
- package/src/utils/create-reducer.test.mts +12 -0
- package/src/utils/create-state.mts +7 -0
- package/src/utils/create-state.test.mts +20 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { source } from '../create/index.mjs';
|
|
2
|
+
import { withBufferedFrom } from './with-buffered-from.mjs';
|
|
3
|
+
|
|
4
|
+
describe('withBufferedFrom teardown', () => {
|
|
5
|
+
test('unsubscribes from the buffered observable on complete', () => {
|
|
6
|
+
const data$ = source<string>();
|
|
7
|
+
|
|
8
|
+
const trigger$ = source<number>();
|
|
9
|
+
|
|
10
|
+
const result$ = trigger$.pipe(withBufferedFrom(data$));
|
|
11
|
+
|
|
12
|
+
assert.isTrue(data$.hasSubscriber);
|
|
13
|
+
|
|
14
|
+
result$.complete();
|
|
15
|
+
|
|
16
|
+
assert.isTrue(result$.isCompleted);
|
|
17
|
+
|
|
18
|
+
assert.isFalse(data$.hasSubscriber);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('unsubscribes from the buffered observable when the parent completes', () => {
|
|
22
|
+
const data$ = source<string>();
|
|
23
|
+
|
|
24
|
+
const trigger$ = source<number>();
|
|
25
|
+
|
|
26
|
+
const result$ = trigger$.pipe(withBufferedFrom(data$));
|
|
27
|
+
|
|
28
|
+
trigger$.complete();
|
|
29
|
+
|
|
30
|
+
assert.isTrue(result$.isCompleted);
|
|
31
|
+
|
|
32
|
+
assert.isFalse(data$.hasSubscriber);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('keeps buffering while subscribed', () => {
|
|
36
|
+
const data$ = source<string>();
|
|
37
|
+
|
|
38
|
+
const trigger$ = source<number>();
|
|
39
|
+
|
|
40
|
+
const result$ = trigger$.pipe(withBufferedFrom(data$));
|
|
41
|
+
|
|
42
|
+
const mut_history: (readonly [number, readonly string[]])[] = [];
|
|
43
|
+
|
|
44
|
+
result$.subscribe((v) => {
|
|
45
|
+
mut_history.push(v);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
data$.next('A');
|
|
49
|
+
|
|
50
|
+
data$.next('B');
|
|
51
|
+
|
|
52
|
+
trigger$.next(1);
|
|
53
|
+
|
|
54
|
+
assert.deepStrictEqual(mut_history, [[1, ['A', 'B']]]);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -20,7 +20,11 @@ import type { NonEmptyUnknownList } from './types.mjs';
|
|
|
20
20
|
// RootObservable
|
|
21
21
|
|
|
22
22
|
export type InitializedSourceObservable<A> = Readonly<{
|
|
23
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Passes `value` on and returns it. Once completed, passes nothing on and
|
|
25
|
+
* returns the value held.
|
|
26
|
+
*/
|
|
27
|
+
next: (value: A) => A;
|
|
24
28
|
}> &
|
|
25
29
|
InitializedRootObservable<A>;
|
|
26
30
|
|
|
@@ -18,13 +18,13 @@ import { source, type Observable } from '../core/index.mjs';
|
|
|
18
18
|
*
|
|
19
19
|
* emitClick(); // logs: Clicked!
|
|
20
20
|
*
|
|
21
|
-
* assert.
|
|
21
|
+
* assert.strictEqual(mut_clickCount.value, 1);
|
|
22
22
|
*
|
|
23
23
|
* emitClick();
|
|
24
24
|
*
|
|
25
25
|
* emitClick();
|
|
26
26
|
*
|
|
27
|
-
* assert.
|
|
27
|
+
* assert.strictEqual(mut_clickCount.value, 3);
|
|
28
28
|
* ```
|
|
29
29
|
*/
|
|
30
30
|
export const createEventEmitter = (): readonly [
|
|
@@ -11,7 +11,9 @@ import { source, type InitializedObservable } from '../core/index.mjs';
|
|
|
11
11
|
* @param initialState - The initial value of the state
|
|
12
12
|
* @param options - {@link StateOptions}: `equals`, to pass on only the
|
|
13
13
|
* dispatches that change the state
|
|
14
|
-
* @returns A 3-element tuple: `[state, dispatch, { getSnapshot, initialState }]
|
|
14
|
+
* @returns A 3-element tuple: `[state, dispatch, { getSnapshot, initialState }]`.
|
|
15
|
+
* `dispatch` returns the state it leaves; once `state` is completed, that
|
|
16
|
+
* is the state held.
|
|
15
17
|
*
|
|
16
18
|
* @example
|
|
17
19
|
* ```ts
|
|
@@ -67,6 +69,13 @@ import { source, type InitializedObservable } from '../core/index.mjs';
|
|
|
67
69
|
*
|
|
68
70
|
* assert.deepStrictEqual(countHistory, [0, 2]);
|
|
69
71
|
* ```
|
|
72
|
+
*
|
|
73
|
+
* @note Do not call `dispatch` from a subscriber or a projection while another
|
|
74
|
+
* update is propagating: the write propagates immediately, so subscribers can
|
|
75
|
+
* see an intermediate state, and it logs a `console.warn` (once per
|
|
76
|
+
* observable). Derive the value from its source instead —
|
|
77
|
+
* `const b$ = a$.pipe(map((a) => a * 2))` rather than
|
|
78
|
+
* `a$.subscribe((a) => { setB(a * 2); })`.
|
|
70
79
|
*/
|
|
71
80
|
export const createReducer = <S, A>(
|
|
72
81
|
reducer: Reducer<S, A>,
|
|
@@ -89,13 +98,10 @@ export const createReducer = <S, A>(
|
|
|
89
98
|
|
|
90
99
|
const nextState = reducer(currentState, action);
|
|
91
100
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
state.next(nextState);
|
|
97
|
-
|
|
98
|
-
return nextState;
|
|
101
|
+
// Once completed, `next` keeps the state held and returns it.
|
|
102
|
+
return equals?.(currentState, nextState) === true
|
|
103
|
+
? currentState
|
|
104
|
+
: state.next(nextState);
|
|
99
105
|
};
|
|
100
106
|
|
|
101
107
|
const getSnapshot = (): S => state.getSnapshot().value;
|
|
@@ -65,6 +65,18 @@ describe(createReducer, () => {
|
|
|
65
65
|
|
|
66
66
|
assert.deepStrictEqual(mut_asked, [[0, 5]]);
|
|
67
67
|
});
|
|
68
|
+
|
|
69
|
+
test('once the state is completed, a dispatch returns the current state', () => {
|
|
70
|
+
const [state, dispatch, { getSnapshot }] = createReducer(keepOrAdd, 0);
|
|
71
|
+
|
|
72
|
+
dispatch({ type: 'add', amount: 1 });
|
|
73
|
+
|
|
74
|
+
state.complete();
|
|
75
|
+
|
|
76
|
+
assert.strictEqual(dispatch({ type: 'add', amount: 2 }), 1);
|
|
77
|
+
|
|
78
|
+
assert.strictEqual(getSnapshot(), 1);
|
|
79
|
+
});
|
|
68
80
|
});
|
|
69
81
|
|
|
70
82
|
type Count = Readonly<{ count: number }>;
|
|
@@ -88,6 +88,13 @@ const reducer = <S,>(state: S, action: Action<S>): S => {
|
|
|
88
88
|
* 'label:bug page:2',
|
|
89
89
|
* ]);
|
|
90
90
|
* ```
|
|
91
|
+
*
|
|
92
|
+
* @note Do not call `setState`, `updateState` or `resetState` from a subscriber or a projection while another
|
|
93
|
+
* update is propagating: the write propagates immediately, so subscribers can
|
|
94
|
+
* see an intermediate state, and it logs a `console.warn` (once per
|
|
95
|
+
* observable). Derive the value from its source instead —
|
|
96
|
+
* `const b$ = a$.pipe(map((a) => a * 2))` rather than
|
|
97
|
+
* `a$.subscribe((a) => { setB(a * 2); })`.
|
|
91
98
|
*/
|
|
92
99
|
export const createState = <S,>(
|
|
93
100
|
initialState: S,
|
|
@@ -34,6 +34,26 @@ describe(createState, () => {
|
|
|
34
34
|
assert.deepStrictEqual(seen, [0, 1]);
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
+
test('once the state is completed, setState, updateState and resetState return the current value', () => {
|
|
38
|
+
const [state, setState, { updateState, resetState, getSnapshot }] =
|
|
39
|
+
createState(0);
|
|
40
|
+
|
|
41
|
+
setState(1);
|
|
42
|
+
|
|
43
|
+
state.complete();
|
|
44
|
+
|
|
45
|
+
assert.strictEqual(setState(2), 1);
|
|
46
|
+
|
|
47
|
+
assert.strictEqual(
|
|
48
|
+
updateState((current) => current + 1),
|
|
49
|
+
1,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
assert.strictEqual(resetState(), 1);
|
|
53
|
+
|
|
54
|
+
assert.strictEqual(getSnapshot(), 1);
|
|
55
|
+
});
|
|
56
|
+
|
|
37
57
|
// What `equals` is for: whatever reads the state, directly or through
|
|
38
58
|
// anything derived from it, is told only of a real change, and an equal
|
|
39
59
|
// value written over the current one leaves the object that is there.
|