synstate 4.0.0 → 4.1.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 +1 -1
- package/dist/core/create/empty.d.mts +32 -0
- package/dist/core/create/empty.d.mts.map +1 -0
- package/dist/core/create/empty.mjs +39 -0
- package/dist/core/create/empty.mjs.map +1 -0
- package/dist/core/create/index.d.mts +1 -0
- package/dist/core/create/index.d.mts.map +1 -1
- package/dist/core/create/index.mjs +1 -0
- package/dist/core/create/index.mjs.map +1 -1
- package/package.json +9 -9
- package/src/core/create/empty.mts +42 -0
- package/src/core/create/empty.test.mts +65 -0
- package/src/core/create/index.mts +1 -0
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
- 🎯 **Simple State Management**: Easy-to-use `createState` and `createReducer` similar to React useState/useReducer for global state
|
|
25
25
|
- ⚡ **High Performance**: Optimized for fast state updates and minimal re-renders
|
|
26
26
|
- 🎨 **Type-Safe**: Full TypeScript support with precise type inference
|
|
27
|
-
- 🚀 **Lightweight**: <!-- bundle-size:synstate -->~6 kB min+gzip<!-- /bundle-size:synstate --> with two external runtime dependencies ([ts-data-forge](https://www.npmjs.com/package/ts-data-forge) and [ts-std-forge](https://www.npmjs.com/package/ts-std-forge), and the first depends on the second)
|
|
27
|
+
- 🚀 **Lightweight**: <!-- bundle-size:synstate -->~6.1 kB min+gzip<!-- /bundle-size:synstate --> with two external runtime dependencies ([ts-data-forge](https://www.npmjs.com/package/ts-data-forge) and [ts-std-forge](https://www.npmjs.com/package/ts-std-forge), and the first depends on the second)
|
|
28
28
|
- 🌐 **Framework Agnostic**: Works with React, Vue, Svelte, or vanilla JavaScript
|
|
29
29
|
- 🔄 **Reactive Updates**: Automatic propagation of state changes to all subscribers
|
|
30
30
|
- 📡 **Event System**: Built-in `createValueEmitter`, `createEventEmitter` for event-driven architecture
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { RootObservable } from '../types/index.mjs';
|
|
2
|
+
/**
|
|
3
|
+
* Creates an Observable that holds no value and is already completed, so it
|
|
4
|
+
* never passes anything on (almost equivalent to RxJS `EMPTY`). Useful inside
|
|
5
|
+
* `switchMap` for the branch that should follow nothing, where a `source()`
|
|
6
|
+
* would offer a `next` that nothing calls.
|
|
7
|
+
*
|
|
8
|
+
* @template A - The type of the values it would carry
|
|
9
|
+
* @returns An Observable with no value that is already completed
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* const nothing$ = empty<number>();
|
|
14
|
+
*
|
|
15
|
+
* const valueHistory: number[] = [];
|
|
16
|
+
*
|
|
17
|
+
* nothing$.subscribe(
|
|
18
|
+
* (value) => {
|
|
19
|
+
* valueHistory.push(value);
|
|
20
|
+
* },
|
|
21
|
+
* () => {
|
|
22
|
+
* // onComplete — called immediately since empty() completes right away
|
|
23
|
+
* },
|
|
24
|
+
* );
|
|
25
|
+
*
|
|
26
|
+
* assert.deepStrictEqual(valueHistory, []);
|
|
27
|
+
*
|
|
28
|
+
* assert.strictEqual(nothing$.isCompleted, true);
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare const empty: <A>() => RootObservable<A>;
|
|
32
|
+
//# sourceMappingURL=empty.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"empty.d.mts","sourceRoot":"","sources":["../../../src/core/create/empty.mts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,KAAK,GAAI,CAAC,OAAM,cAAc,CAAC,CAAC,CAQ5C,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Optional } from 'ts-data-forge';
|
|
2
|
+
import { RootObservableClass } from '../class/index.mjs';
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
export const empty = () => {
|
|
33
|
+
const obs = new RootObservableClass({
|
|
34
|
+
initialValue: Optional.none,
|
|
35
|
+
});
|
|
36
|
+
obs.complete();
|
|
37
|
+
return obs;
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=empty.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"empty.mjs","sourceRoot":"","sources":["../../../src/core/create/empty.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAGzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,GAA0B,EAAE;IAC/C,MAAM,GAAG,GAAG,IAAI,mBAAmB,CAAI;QACrC,YAAY,EAAE,QAAQ,CAAC,IAAI;KAC5B,CAAC,CAAC;IAEH,GAAG,CAAC,QAAQ,EAAE,CAAC;IAEf,OAAO,GAAG,CAAC;AACb,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../../../src/core/create/index.mts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,8BAA8B,CAAC;AAC7C,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../../../src/core/create/index.mts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,8BAA8B,CAAC;AAC7C,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sourceRoot":"","sources":["../../../src/core/create/index.mts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,8BAA8B,CAAC;AAC7C,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.mjs","sourceRoot":"","sources":["../../../src/core/create/index.mts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,8BAA8B,CAAC;AAC7C,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "synstate",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "Type-safe State Management Library for TypeScript/JavaScript",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -29,20 +29,20 @@
|
|
|
29
29
|
"LICENSE"
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"ts-data-forge": "^14.
|
|
33
|
-
"ts-std-forge": "^0.5.
|
|
32
|
+
"ts-data-forge": "^14.9.0",
|
|
33
|
+
"ts-std-forge": "^0.5.2",
|
|
34
34
|
"ts-type-forge": "^9.2.2"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@reduxjs/toolkit": "2.13.0",
|
|
38
|
-
"@sumi-lang/cli": "0.0.
|
|
38
|
+
"@sumi-lang/cli": "0.0.7",
|
|
39
39
|
"@types/react": "19.3.0",
|
|
40
40
|
"@vitest/browser-playwright": "5.0.3",
|
|
41
41
|
"argparse": "3.0.2",
|
|
42
42
|
"eslint-config-typed": "5.19.0",
|
|
43
|
-
"eslint-plugin-ts-data-forge": "0.
|
|
44
|
-
"eslint-plugin-ts-fortress": "1.3.
|
|
45
|
-
"eslint-plugin-ts-type-forge": "0.8.
|
|
43
|
+
"eslint-plugin-ts-data-forge": "0.10.0",
|
|
44
|
+
"eslint-plugin-ts-fortress": "1.3.5",
|
|
45
|
+
"eslint-plugin-ts-type-forge": "0.8.10",
|
|
46
46
|
"jotai": "3.0.1",
|
|
47
47
|
"mobx": "7.0.6",
|
|
48
48
|
"playwright": "1.64.0",
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
"prettier-plugin-packagejson": "3.0.2",
|
|
52
52
|
"react": "19.3.0",
|
|
53
53
|
"rxjs": "7.8.2",
|
|
54
|
-
"synstate-react-hooks": "4.
|
|
55
|
-
"synstate-react-hooks-compat": "4.
|
|
54
|
+
"synstate-react-hooks": "4.1.0",
|
|
55
|
+
"synstate-react-hooks-compat": "4.1.0",
|
|
56
56
|
"ts-repo-utils": "10.7.1",
|
|
57
57
|
"valtio": "2.3.2",
|
|
58
58
|
"zustand": "5.0.15"
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Optional } from 'ts-data-forge';
|
|
2
|
+
import { RootObservableClass } from '../class/index.mjs';
|
|
3
|
+
import type { RootObservable } from '../types/index.mjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Creates an Observable that holds no value and is already completed, so it
|
|
7
|
+
* never passes anything on (almost equivalent to RxJS `EMPTY`). Useful inside
|
|
8
|
+
* `switchMap` for the branch that should follow nothing, where a `source()`
|
|
9
|
+
* would offer a `next` that nothing calls.
|
|
10
|
+
*
|
|
11
|
+
* @template A - The type of the values it would carry
|
|
12
|
+
* @returns An Observable with no value that is already completed
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* const nothing$ = empty<number>();
|
|
17
|
+
*
|
|
18
|
+
* const valueHistory: number[] = [];
|
|
19
|
+
*
|
|
20
|
+
* nothing$.subscribe(
|
|
21
|
+
* (value) => {
|
|
22
|
+
* valueHistory.push(value);
|
|
23
|
+
* },
|
|
24
|
+
* () => {
|
|
25
|
+
* // onComplete — called immediately since empty() completes right away
|
|
26
|
+
* },
|
|
27
|
+
* );
|
|
28
|
+
*
|
|
29
|
+
* assert.deepStrictEqual(valueHistory, []);
|
|
30
|
+
*
|
|
31
|
+
* assert.strictEqual(nothing$.isCompleted, true);
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export const empty = <A,>(): RootObservable<A> => {
|
|
35
|
+
const obs = new RootObservableClass<A>({
|
|
36
|
+
initialValue: Optional.none,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
obs.complete();
|
|
40
|
+
|
|
41
|
+
return obs;
|
|
42
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Arr, Optional } from 'ts-data-forge';
|
|
2
|
+
import { createState } from '../../utils/index.mjs';
|
|
3
|
+
import { switchMap } from '../operators/index.mjs';
|
|
4
|
+
import { empty } from './empty.mjs';
|
|
5
|
+
|
|
6
|
+
describe(empty, () => {
|
|
7
|
+
test('holds no value and is already completed', () => {
|
|
8
|
+
const nothing$ = empty<number>();
|
|
9
|
+
|
|
10
|
+
assert.isTrue(nothing$.isCompleted);
|
|
11
|
+
|
|
12
|
+
assert.isTrue(Optional.isNone(nothing$.getSnapshot()));
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('passes nothing on to a subscriber, and completes it at once', () => {
|
|
16
|
+
const nothing$ = empty<number>();
|
|
17
|
+
|
|
18
|
+
const mut_seen: number[] = [];
|
|
19
|
+
|
|
20
|
+
const mut_completed = { value: false };
|
|
21
|
+
|
|
22
|
+
nothing$.subscribe(
|
|
23
|
+
(value) => {
|
|
24
|
+
mut_seen.push(value);
|
|
25
|
+
},
|
|
26
|
+
() => {
|
|
27
|
+
mut_completed.value = true;
|
|
28
|
+
},
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
assert.deepStrictEqual(mut_seen, []);
|
|
32
|
+
|
|
33
|
+
assert.isTrue(mut_completed.value);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('switched to, it passes nothing on, and switchMap goes on', () => {
|
|
37
|
+
const [on$, setOn] = createState(true);
|
|
38
|
+
|
|
39
|
+
const [count$, setCount] = createState(0);
|
|
40
|
+
|
|
41
|
+
const followed$ = on$.pipe(
|
|
42
|
+
switchMap((isOn) => (isOn ? count$ : empty<number>())),
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const mut_seen: number[] = [];
|
|
46
|
+
|
|
47
|
+
followed$.subscribe((value) => {
|
|
48
|
+
mut_seen.push(value);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
setOn(false);
|
|
52
|
+
|
|
53
|
+
const seenBefore = Arr.length(mut_seen);
|
|
54
|
+
|
|
55
|
+
setCount(1);
|
|
56
|
+
|
|
57
|
+
assert.strictEqual(Arr.length(mut_seen), seenBefore);
|
|
58
|
+
|
|
59
|
+
setOn(true);
|
|
60
|
+
|
|
61
|
+
assert.deepStrictEqual(Arr.last(mut_seen), Optional.some(1));
|
|
62
|
+
|
|
63
|
+
assert.isFalse(followed$.isCompleted);
|
|
64
|
+
});
|
|
65
|
+
});
|