@push.rocks/smartstate 2.0.23 → 2.0.25
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_bundle/bundle.js +51 -23
- package/dist_bundle/bundle.js.map +2 -2
- package/dist_ts/smartstate.classes.smartstate.d.ts +5 -3
- package/dist_ts/smartstate.classes.smartstate.js +24 -12
- package/dist_ts/smartstate.classes.statepart.d.ts +8 -3
- package/dist_ts/smartstate.classes.statepart.js +33 -13
- package/package.json +8 -7
- package/readme.hints.md +29 -16
- package/readme.md +108 -18
- package/ts/smartstate.classes.smartstate.ts +26 -13
- package/ts/smartstate.classes.statepart.ts +35 -12
|
@@ -10,9 +10,10 @@ export declare class Smartstate<StatePartNameType extends string> {
|
|
|
10
10
|
constructor();
|
|
11
11
|
/**
|
|
12
12
|
* Allows getting and initializing a new statepart
|
|
13
|
-
* initMode === 'soft'
|
|
14
|
-
* initMode === 'mandatory'
|
|
15
|
-
* initMode === 'force'
|
|
13
|
+
* initMode === 'soft' (default) - returns existing statepart if exists, creates new if not
|
|
14
|
+
* initMode === 'mandatory' - requires statepart to not exist, fails if it does
|
|
15
|
+
* initMode === 'force' - always creates new statepart, overwriting any existing
|
|
16
|
+
* initMode === 'persistent' - like 'soft' but with webstore persistence
|
|
16
17
|
* @param statePartNameArg
|
|
17
18
|
* @param initialArg
|
|
18
19
|
* @param initMode
|
|
@@ -22,6 +23,7 @@ export declare class Smartstate<StatePartNameType extends string> {
|
|
|
22
23
|
* Creates a statepart
|
|
23
24
|
* @param statePartName
|
|
24
25
|
* @param initialPayloadArg
|
|
26
|
+
* @param initMode
|
|
25
27
|
*/
|
|
26
28
|
private createStatePart;
|
|
27
29
|
}
|
|
@@ -9,23 +9,34 @@ export class Smartstate {
|
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
11
|
* Allows getting and initializing a new statepart
|
|
12
|
-
* initMode === 'soft'
|
|
13
|
-
* initMode === 'mandatory'
|
|
14
|
-
* initMode === 'force'
|
|
12
|
+
* initMode === 'soft' (default) - returns existing statepart if exists, creates new if not
|
|
13
|
+
* initMode === 'mandatory' - requires statepart to not exist, fails if it does
|
|
14
|
+
* initMode === 'force' - always creates new statepart, overwriting any existing
|
|
15
|
+
* initMode === 'persistent' - like 'soft' but with webstore persistence
|
|
15
16
|
* @param statePartNameArg
|
|
16
17
|
* @param initialArg
|
|
17
18
|
* @param initMode
|
|
18
19
|
*/
|
|
19
|
-
async getStatePart(statePartNameArg, initialArg, initMode) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
async getStatePart(statePartNameArg, initialArg, initMode = 'soft') {
|
|
21
|
+
const existingStatePart = this.statePartMap[statePartNameArg];
|
|
22
|
+
if (existingStatePart) {
|
|
23
|
+
switch (initMode) {
|
|
24
|
+
case 'mandatory':
|
|
25
|
+
throw new Error(`State part '${statePartNameArg}' already exists, but initMode is 'mandatory'`);
|
|
26
|
+
case 'force':
|
|
27
|
+
// Force mode: create new state part
|
|
28
|
+
return this.createStatePart(statePartNameArg, initialArg, initMode);
|
|
29
|
+
case 'soft':
|
|
30
|
+
case 'persistent':
|
|
31
|
+
default:
|
|
32
|
+
// Return existing state part
|
|
33
|
+
return existingStatePart;
|
|
23
34
|
}
|
|
24
|
-
return this.statePartMap[statePartNameArg];
|
|
25
35
|
}
|
|
26
36
|
else {
|
|
37
|
+
// State part doesn't exist
|
|
27
38
|
if (!initialArg) {
|
|
28
|
-
throw new Error(
|
|
39
|
+
throw new Error(`State part '${statePartNameArg}' does not exist and no initial state provided`);
|
|
29
40
|
}
|
|
30
41
|
return this.createStatePart(statePartNameArg, initialArg, initMode);
|
|
31
42
|
}
|
|
@@ -34,8 +45,9 @@ export class Smartstate {
|
|
|
34
45
|
* Creates a statepart
|
|
35
46
|
* @param statePartName
|
|
36
47
|
* @param initialPayloadArg
|
|
48
|
+
* @param initMode
|
|
37
49
|
*/
|
|
38
|
-
async createStatePart(statePartName, initialPayloadArg, initMode) {
|
|
50
|
+
async createStatePart(statePartName, initialPayloadArg, initMode = 'soft') {
|
|
39
51
|
const newState = new StatePart(statePartName, initMode === 'persistent'
|
|
40
52
|
? {
|
|
41
53
|
dbName: 'smartstate',
|
|
@@ -45,11 +57,11 @@ export class Smartstate {
|
|
|
45
57
|
await newState.init();
|
|
46
58
|
const currentState = newState.getState();
|
|
47
59
|
await newState.setState({
|
|
48
|
-
...initialPayloadArg,
|
|
49
60
|
...currentState,
|
|
61
|
+
...initialPayloadArg,
|
|
50
62
|
});
|
|
51
63
|
this.statePartMap[statePartName] = newState;
|
|
52
64
|
return newState;
|
|
53
65
|
}
|
|
54
66
|
}
|
|
55
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
67
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRzdGF0ZS5jbGFzc2VzLnNtYXJ0c3RhdGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydHN0YXRlLmNsYXNzZXMuc21hcnRzdGF0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHlCQUF5QixDQUFDO0FBQ25ELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUk5RDs7R0FFRztBQUNILE1BQU0sT0FBTyxVQUFVO0lBR3JCO1FBRk8saUJBQVksR0FBdUUsRUFBRSxDQUFDO0lBRTlFLENBQUM7SUFFaEI7Ozs7Ozs7OztPQVNHO0lBQ0ksS0FBSyxDQUFDLFlBQVksQ0FDdkIsZ0JBQW1DLEVBQ25DLFVBQXdCLEVBQ3hCLFdBQXNCLE1BQU07UUFFNUIsTUFBTSxpQkFBaUIsR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDLGdCQUFnQixDQUFDLENBQUM7UUFFOUQsSUFBSSxpQkFBaUIsRUFBRSxDQUFDO1lBQ3RCLFFBQVEsUUFBUSxFQUFFLENBQUM7Z0JBQ2pCLEtBQUssV0FBVztvQkFDZCxNQUFNLElBQUksS0FBSyxDQUNiLGVBQWUsZ0JBQWdCLCtDQUErQyxDQUMvRSxDQUFDO2dCQUNKLEtBQUssT0FBTztvQkFDVixvQ0FBb0M7b0JBQ3BDLE9BQU8sSUFBSSxDQUFDLGVBQWUsQ0FBYyxnQkFBZ0IsRUFBRSxVQUFVLEVBQUUsUUFBUSxDQUFDLENBQUM7Z0JBQ25GLEtBQUssTUFBTSxDQUFDO2dCQUNaLEtBQUssWUFBWSxDQUFDO2dCQUNsQjtvQkFDRSw2QkFBNkI7b0JBQzdCLE9BQU8saUJBQThELENBQUM7WUFDMUUsQ0FBQztRQUNILENBQUM7YUFBTSxDQUFDO1lBQ04sMkJBQTJCO1lBQzNCLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztnQkFDaEIsTUFBTSxJQUFJLEtBQUssQ0FDYixlQUFlLGdCQUFnQixnREFBZ0QsQ0FDaEYsQ0FBQztZQUNKLENBQUM7WUFDRCxPQUFPLElBQUksQ0FBQyxlQUFlLENBQWMsZ0JBQWdCLEVBQUUsVUFBVSxFQUFFLFFBQVEsQ0FBQyxDQUFDO1FBQ25GLENBQUM7SUFDSCxDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSyxLQUFLLENBQUMsZUFBZSxDQUMzQixhQUFnQyxFQUNoQyxpQkFBOEIsRUFDOUIsV0FBc0IsTUFBTTtRQUU1QixNQUFNLFFBQVEsR0FBRyxJQUFJLFNBQVMsQ0FDNUIsYUFBYSxFQUNiLFFBQVEsS0FBSyxZQUFZO1lBQ3ZCLENBQUMsQ0FBQztnQkFDRSxNQUFNLEVBQUUsWUFBWTtnQkFDcEIsU0FBUyxFQUFFLGFBQWE7YUFDekI7WUFDSCxDQUFDLENBQUMsSUFBSSxDQUNULENBQUM7UUFDRixNQUFNLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUN0QixNQUFNLFlBQVksR0FBRyxRQUFRLENBQUMsUUFBUSxFQUFFLENBQUM7UUFDekMsTUFBTSxRQUFRLENBQUMsUUFBUSxDQUFDO1lBQ3RCLEdBQUcsWUFBWTtZQUNmLEdBQUcsaUJBQWlCO1NBQ3JCLENBQUMsQ0FBQztRQUNILElBQUksQ0FBQyxZQUFZLENBQUMsYUFBYSxDQUFDLEdBQUcsUUFBUSxDQUFDO1FBQzVDLE9BQU8sUUFBUSxDQUFDO0lBQ2xCLENBQUM7Q0FDRiJ9
|
|
@@ -3,7 +3,7 @@ import { StateAction, type IActionDef } from './smartstate.classes.stateaction.j
|
|
|
3
3
|
export declare class StatePart<TStatePartName, TStatePayload> {
|
|
4
4
|
name: TStatePartName;
|
|
5
5
|
state: plugins.smartrx.rxjs.Subject<TStatePayload>;
|
|
6
|
-
stateStore: TStatePayload;
|
|
6
|
+
stateStore: TStatePayload | undefined;
|
|
7
7
|
private cumulativeDeferred;
|
|
8
8
|
private webStoreOptions;
|
|
9
9
|
private webStore;
|
|
@@ -15,16 +15,21 @@ export declare class StatePart<TStatePartName, TStatePayload> {
|
|
|
15
15
|
/**
|
|
16
16
|
* gets the state from the state store
|
|
17
17
|
*/
|
|
18
|
-
getState(): TStatePayload;
|
|
18
|
+
getState(): TStatePayload | undefined;
|
|
19
19
|
/**
|
|
20
20
|
* sets the stateStore to the new state
|
|
21
21
|
* @param newStateArg
|
|
22
22
|
*/
|
|
23
23
|
setState(newStateArg: TStatePayload): Promise<TStatePayload>;
|
|
24
|
+
/**
|
|
25
|
+
* Validates state structure - can be overridden for custom validation
|
|
26
|
+
* @param stateArg
|
|
27
|
+
*/
|
|
28
|
+
protected validateState(stateArg: any): stateArg is TStatePayload;
|
|
24
29
|
/**
|
|
25
30
|
* notifies of a change on the state
|
|
26
31
|
*/
|
|
27
|
-
notifyChange(): void
|
|
32
|
+
notifyChange(): Promise<void>;
|
|
28
33
|
private lastStateNotificationPayloadHash;
|
|
29
34
|
/**
|
|
30
35
|
* creates a cumulative notification by adding a change notification at the end of the call stack;
|
|
@@ -19,9 +19,9 @@ export class StatePart {
|
|
|
19
19
|
this.webStore = new plugins.webstore.WebStore(this.webStoreOptions);
|
|
20
20
|
await this.webStore.init();
|
|
21
21
|
const storedState = await this.webStore.get(String(this.name));
|
|
22
|
-
if (storedState) {
|
|
22
|
+
if (storedState && this.validateState(storedState)) {
|
|
23
23
|
this.stateStore = storedState;
|
|
24
|
-
this.notifyChange();
|
|
24
|
+
await this.notifyChange();
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
}
|
|
@@ -36,28 +36,44 @@ export class StatePart {
|
|
|
36
36
|
* @param newStateArg
|
|
37
37
|
*/
|
|
38
38
|
async setState(newStateArg) {
|
|
39
|
+
// Validate state structure
|
|
40
|
+
if (!this.validateState(newStateArg)) {
|
|
41
|
+
throw new Error(`Invalid state structure for state part '${this.name}'`);
|
|
42
|
+
}
|
|
39
43
|
this.stateStore = newStateArg;
|
|
40
|
-
this.notifyChange();
|
|
44
|
+
await this.notifyChange();
|
|
41
45
|
// Save state to WebStore if initialized
|
|
42
46
|
if (this.webStore) {
|
|
43
47
|
await this.webStore.set(String(this.name), newStateArg);
|
|
44
48
|
}
|
|
45
49
|
return this.stateStore;
|
|
46
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Validates state structure - can be overridden for custom validation
|
|
53
|
+
* @param stateArg
|
|
54
|
+
*/
|
|
55
|
+
validateState(stateArg) {
|
|
56
|
+
// Basic validation - ensure state is not null/undefined
|
|
57
|
+
// Subclasses can override for more specific validation
|
|
58
|
+
return stateArg !== null && stateArg !== undefined;
|
|
59
|
+
}
|
|
47
60
|
/**
|
|
48
61
|
* notifies of a change on the state
|
|
49
62
|
*/
|
|
50
|
-
notifyChange() {
|
|
51
|
-
|
|
52
|
-
return
|
|
63
|
+
async notifyChange() {
|
|
64
|
+
if (!this.stateStore) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const createStateHash = async (stateArg) => {
|
|
68
|
+
return await plugins.smarthashWeb.sha256FromString(plugins.smartjson.stringify(stateArg));
|
|
53
69
|
};
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
70
|
+
const currentHash = await createStateHash(this.stateStore);
|
|
71
|
+
if (this.lastStateNotificationPayloadHash &&
|
|
72
|
+
currentHash === this.lastStateNotificationPayloadHash) {
|
|
57
73
|
return;
|
|
58
74
|
}
|
|
59
75
|
else {
|
|
60
|
-
this.lastStateNotificationPayloadHash =
|
|
76
|
+
this.lastStateNotificationPayloadHash = currentHash;
|
|
61
77
|
}
|
|
62
78
|
this.state.next(this.stateStore);
|
|
63
79
|
}
|
|
@@ -66,7 +82,11 @@ export class StatePart {
|
|
|
66
82
|
*/
|
|
67
83
|
notifyChangeCumulative() {
|
|
68
84
|
// TODO: check viability
|
|
69
|
-
setTimeout(() =>
|
|
85
|
+
setTimeout(async () => {
|
|
86
|
+
if (this.stateStore) {
|
|
87
|
+
await this.notifyChange();
|
|
88
|
+
}
|
|
89
|
+
}, 0);
|
|
70
90
|
}
|
|
71
91
|
/**
|
|
72
92
|
* selects a state or a substate
|
|
@@ -75,7 +95,7 @@ export class StatePart {
|
|
|
75
95
|
if (!selectorFn) {
|
|
76
96
|
selectorFn = (state) => state;
|
|
77
97
|
}
|
|
78
|
-
const mapped = this.state.pipe(plugins.smartrx.rxjs.ops.startWith(this.getState()), plugins.smartrx.rxjs.ops.map((stateArg) => {
|
|
98
|
+
const mapped = this.state.pipe(plugins.smartrx.rxjs.ops.startWith(this.getState()), plugins.smartrx.rxjs.ops.filter((stateArg) => stateArg !== undefined), plugins.smartrx.rxjs.ops.map((stateArg) => {
|
|
79
99
|
try {
|
|
80
100
|
return selectorFn(stateArg);
|
|
81
101
|
}
|
|
@@ -125,4 +145,4 @@ export class StatePart {
|
|
|
125
145
|
this.setState(await resultPromise);
|
|
126
146
|
}
|
|
127
147
|
}
|
|
128
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
148
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRzdGF0ZS5jbGFzc2VzLnN0YXRlcGFydC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYXJ0c3RhdGUuY2xhc3Nlcy5zdGF0ZXBhcnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSx5QkFBeUIsQ0FBQztBQUNuRCxPQUFPLEVBQUUsV0FBVyxFQUFtQixNQUFNLHFDQUFxQyxDQUFDO0FBRW5GLE1BQU0sT0FBTyxTQUFTO0lBU3BCLFlBQVksT0FBdUIsRUFBRSxrQkFBc0Q7UUFQcEYsVUFBSyxHQUFHLElBQUksT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFpQixDQUFDO1FBRXpELHVCQUFrQixHQUFHLE9BQU8sQ0FBQyxZQUFZLENBQUMsZUFBZSxFQUFFLENBQUM7UUFHNUQsYUFBUSxHQUFvRCxJQUFJLENBQUMsQ0FBQyx3QkFBd0I7UUFHaEcsSUFBSSxDQUFDLElBQUksR0FBRyxPQUFPLENBQUM7UUFFcEIsc0RBQXNEO1FBQ3RELElBQUksa0JBQWtCLEVBQUUsQ0FBQztZQUN2QixJQUFJLENBQUMsZUFBZSxHQUFHLGtCQUFrQixDQUFDO1FBQzVDLENBQUM7SUFDSCxDQUFDO0lBRUQ7O09BRUc7SUFDSSxLQUFLLENBQUMsSUFBSTtRQUNmLElBQUksSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDO1lBQ3pCLElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxPQUFPLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBZ0IsSUFBSSxDQUFDLGVBQWUsQ0FBQyxDQUFDO1lBQ25GLE1BQU0sSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUMzQixNQUFNLFdBQVcsR0FBRyxNQUFNLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztZQUMvRCxJQUFJLFdBQVcsSUFBSSxJQUFJLENBQUMsYUFB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
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@push.rocks/smartstate",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.25",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A package for handling and managing state in applications.",
|
|
6
6
|
"main": "dist_ts/index.js",
|
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
"type": "module",
|
|
9
9
|
"author": "Lossless GmbH",
|
|
10
10
|
"license": "MIT",
|
|
11
|
+
"scripts": {
|
|
12
|
+
"test": "(tstest test/ --verbose)",
|
|
13
|
+
"build": "(tsbuild tsfolders --allowimplicitany && tsbundle npm)",
|
|
14
|
+
"buildDocs": "tsdoc"
|
|
15
|
+
},
|
|
11
16
|
"devDependencies": {
|
|
12
17
|
"@git.zone/tsbuild": "^2.6.4",
|
|
13
18
|
"@git.zone/tsbundle": "^2.4.0",
|
|
@@ -56,9 +61,5 @@
|
|
|
56
61
|
"type": "git",
|
|
57
62
|
"url": "https://code.foss.global/push.rocks/smartstate.git"
|
|
58
63
|
},
|
|
59
|
-
"
|
|
60
|
-
|
|
61
|
-
"build": "(tsbuild tsfolders --allowimplicitany && tsbundle npm)",
|
|
62
|
-
"buildDocs": "tsdoc"
|
|
63
|
-
}
|
|
64
|
-
}
|
|
64
|
+
"packageManager": "pnpm@10.11.0+sha512.6540583f41cc5f628eb3d9773ecee802f4f9ef9923cc45b69890fb47991d4b092964694ec3a4f738a420c918a333062c8b925d312f42e4f0c263eb603551f977"
|
|
65
|
+
}
|
package/readme.hints.md
CHANGED
|
@@ -1,39 +1,52 @@
|
|
|
1
1
|
# Smartstate Implementation Notes
|
|
2
2
|
|
|
3
|
-
## Current API (as of
|
|
3
|
+
## Current API (as of v2.0.24+)
|
|
4
4
|
|
|
5
5
|
### State Part Initialization
|
|
6
|
-
- State parts can be created with different init modes: 'soft', 'mandatory', 'force', 'persistent'
|
|
6
|
+
- State parts can be created with different init modes: 'soft' (default), 'mandatory', 'force', 'persistent'
|
|
7
|
+
- 'soft' - returns existing state part if exists, creates new if not
|
|
8
|
+
- 'mandatory' - requires state part to not exist, fails if it does
|
|
9
|
+
- 'force' - always creates new state part, overwriting any existing
|
|
10
|
+
- 'persistent' - like 'soft' but with WebStore persistence (IndexedDB)
|
|
7
11
|
- Persistent mode automatically calls init() internally - no need to call it manually
|
|
8
|
-
-
|
|
12
|
+
- State merge order fixed: initial state takes precedence over stored state
|
|
9
13
|
|
|
10
14
|
### Actions
|
|
11
15
|
- Actions are created with `createAction()` method
|
|
12
16
|
- Two ways to dispatch actions:
|
|
13
17
|
1. `stateAction.trigger(payload)` - returns Promise<TStatePayload>
|
|
14
18
|
2. `await statePart.dispatchAction(stateAction, payload)` - returns Promise<TStatePayload>
|
|
15
|
-
- Both methods
|
|
19
|
+
- Both methods return the same Promise, providing flexibility in usage
|
|
16
20
|
|
|
17
21
|
### State Management Methods
|
|
18
|
-
- `select()` - returns Observable with startWith current state
|
|
22
|
+
- `select()` - returns Observable with startWith current state, filters undefined states
|
|
19
23
|
- `waitUntilPresent()` - waits for specific state condition
|
|
20
24
|
- `stateSetup()` - async state initialization with cumulative defer
|
|
21
|
-
- `notifyChangeCumulative()` - defers notification to end of call stack
|
|
25
|
+
- `notifyChangeCumulative()` - defers notification to end of call stack
|
|
26
|
+
- `getState()` - returns current state or undefined
|
|
27
|
+
- `setState()` - validates state before setting, notifies only on actual changes
|
|
22
28
|
|
|
23
29
|
### State Hash Detection
|
|
24
30
|
- Uses SHA256 hash to detect actual state changes
|
|
25
|
-
-
|
|
26
|
-
-
|
|
31
|
+
- Fixed: Hash comparison now properly awaits async hash calculation
|
|
32
|
+
- Prevents duplicate notifications for identical state values
|
|
33
|
+
- `notifyChange()` is now async to support proper hash comparison
|
|
34
|
+
|
|
35
|
+
### State Validation
|
|
36
|
+
- Basic validation ensures state is not null/undefined
|
|
37
|
+
- `validateState()` method can be overridden in subclasses for custom validation
|
|
38
|
+
- Validation runs on both setState() and when loading from persistent storage
|
|
27
39
|
|
|
28
40
|
### Type System
|
|
29
41
|
- Can use either enums or string literal types for state part names
|
|
30
42
|
- Test uses simple string types: `type TMyStateParts = 'testStatePart'`
|
|
43
|
+
- State can be undefined initially, handled properly in select() and other methods
|
|
31
44
|
|
|
32
|
-
##
|
|
33
|
-
1.
|
|
34
|
-
2.
|
|
35
|
-
3.
|
|
36
|
-
4.
|
|
37
|
-
5. Added
|
|
38
|
-
6.
|
|
39
|
-
7.
|
|
45
|
+
## Recent Fixes (v2.0.24+)
|
|
46
|
+
1. Fixed state hash bug - now properly compares hash values instead of promises
|
|
47
|
+
2. Fixed state initialization merge order - initial state now takes precedence
|
|
48
|
+
3. Ensured stateStore is properly typed as potentially undefined
|
|
49
|
+
4. Simplified init mode logic with clear behavior for each mode
|
|
50
|
+
5. Added state validation with extensible validateState() method
|
|
51
|
+
6. Made notifyChange() async to support proper hash comparison
|
|
52
|
+
7. Updated select() to filter undefined states
|
package/readme.md
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
# @push.rocks/smartstate
|
|
2
|
-
A
|
|
2
|
+
A powerful TypeScript library for elegant state management using RxJS and reactive programming patterns
|
|
3
3
|
|
|
4
4
|
## Install
|
|
5
5
|
|
|
6
|
-
To install `@push.rocks/smartstate`, you can use pnpm
|
|
6
|
+
To install `@push.rocks/smartstate`, you can use pnpm, npm, or yarn:
|
|
7
7
|
|
|
8
8
|
```bash
|
|
9
|
+
# Using pnpm (recommended)
|
|
9
10
|
pnpm install @push.rocks/smartstate --save
|
|
11
|
+
|
|
12
|
+
# Using npm
|
|
13
|
+
npm install @push.rocks/smartstate --save
|
|
14
|
+
|
|
15
|
+
# Using yarn
|
|
16
|
+
yarn add @push.rocks/smartstate
|
|
10
17
|
```
|
|
11
18
|
|
|
12
19
|
This will add `@push.rocks/smartstate` to your project's dependencies.
|
|
@@ -35,10 +42,10 @@ const myAppSmartState = new Smartstate<YourStatePartNamesEnum>();
|
|
|
35
42
|
|
|
36
43
|
When creating state parts, you can specify different initialization modes:
|
|
37
44
|
|
|
38
|
-
- **`'soft'`** -
|
|
39
|
-
- **`'mandatory'`** -
|
|
40
|
-
- **`'force'`** -
|
|
41
|
-
- **`'persistent'`** -
|
|
45
|
+
- **`'soft'`** (default) - Returns existing state part if it exists, creates new if not
|
|
46
|
+
- **`'mandatory'`** - Requires state part to not exist, fails if it does
|
|
47
|
+
- **`'force'`** - Always creates new state part, overwriting any existing one
|
|
48
|
+
- **`'persistent'`** - Like 'soft' but with WebStore persistence using IndexedDB
|
|
42
49
|
|
|
43
50
|
### Defining State Parts
|
|
44
51
|
|
|
@@ -79,6 +86,7 @@ const userStatePart = await myAppSmartState.getStatePart<IUserState>(
|
|
|
79
86
|
You can subscribe to changes in a state part to perform actions accordingly:
|
|
80
87
|
|
|
81
88
|
```typescript
|
|
89
|
+
// The select() method automatically filters out undefined states
|
|
82
90
|
userStatePart.select().subscribe((currentState) => {
|
|
83
91
|
console.log(`User Logged In: ${currentState.isLoggedIn}`);
|
|
84
92
|
});
|
|
@@ -134,6 +142,12 @@ Both methods return a Promise with the new state, giving you flexibility in how
|
|
|
134
142
|
`StatePart` provides several useful methods for state management:
|
|
135
143
|
|
|
136
144
|
```typescript
|
|
145
|
+
// Get current state (may be undefined initially)
|
|
146
|
+
const currentState = userStatePart.getState();
|
|
147
|
+
if (currentState) {
|
|
148
|
+
console.log('Current user:', currentState.username);
|
|
149
|
+
}
|
|
150
|
+
|
|
137
151
|
// Wait for a specific state condition
|
|
138
152
|
await userStatePart.waitUntilPresent();
|
|
139
153
|
|
|
@@ -170,13 +184,33 @@ Persistent state automatically:
|
|
|
170
184
|
- Restores state on application restart
|
|
171
185
|
- Manages storage with configurable database and store names
|
|
172
186
|
|
|
187
|
+
### State Validation
|
|
188
|
+
|
|
189
|
+
`Smartstate` includes built-in state validation to ensure data integrity:
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
// Basic validation (built-in)
|
|
193
|
+
// Ensures state is not null or undefined
|
|
194
|
+
await userStatePart.setState(null); // Throws error: Invalid state structure
|
|
195
|
+
|
|
196
|
+
// Custom validation by extending StatePart
|
|
197
|
+
class ValidatedStatePart<T> extends StatePart<string, T> {
|
|
198
|
+
protected validateState(stateArg: any): stateArg is T {
|
|
199
|
+
// Add your custom validation logic
|
|
200
|
+
return super.validateState(stateArg) && /* your validation */;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
173
205
|
### Performance Optimization
|
|
174
206
|
|
|
175
|
-
`Smartstate` includes
|
|
207
|
+
`Smartstate` includes advanced performance optimizations:
|
|
176
208
|
|
|
177
|
-
- **State
|
|
209
|
+
- **Async State Hash Detection**: Uses SHA256 hashing to detect actual state changes, preventing unnecessary notifications when state values haven't truly changed
|
|
210
|
+
- **Duplicate Prevention**: Identical state updates are automatically filtered out
|
|
178
211
|
- **Cumulative Notifications**: Batch multiple state changes into a single notification using `notifyChangeCumulative()`
|
|
179
212
|
- **Selective Subscriptions**: Use selectors to subscribe only to specific state properties
|
|
213
|
+
- **Undefined State Filtering**: The `select()` method automatically filters out undefined states
|
|
180
214
|
|
|
181
215
|
### RxJS Integration
|
|
182
216
|
|
|
@@ -202,19 +236,75 @@ userStatePart.select(state => state.username)
|
|
|
202
236
|
});
|
|
203
237
|
```
|
|
204
238
|
|
|
205
|
-
###
|
|
239
|
+
### Complete Example
|
|
240
|
+
|
|
241
|
+
Here's a comprehensive example showcasing the power of `@push.rocks/smartstate`:
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
import { Smartstate, StatePart, StateAction } from '@push.rocks/smartstate';
|
|
245
|
+
|
|
246
|
+
// Define your state structure
|
|
247
|
+
type AppStateParts = 'user' | 'settings' | 'cart';
|
|
248
|
+
|
|
249
|
+
interface IUserState {
|
|
250
|
+
isLoggedIn: boolean;
|
|
251
|
+
username?: string;
|
|
252
|
+
email?: string;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
interface ICartState {
|
|
256
|
+
items: Array<{ id: string; quantity: number }>;
|
|
257
|
+
total: number;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Create the smartstate instance
|
|
261
|
+
const appState = new Smartstate<AppStateParts>();
|
|
262
|
+
|
|
263
|
+
// Initialize state parts
|
|
264
|
+
const userState = await appState.getStatePart<IUserState>('user', {
|
|
265
|
+
isLoggedIn: false
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
const cartState = await appState.getStatePart<ICartState>('cart', {
|
|
269
|
+
items: [],
|
|
270
|
+
total: 0
|
|
271
|
+
}, 'persistent'); // Persists across sessions
|
|
272
|
+
|
|
273
|
+
// Create actions
|
|
274
|
+
const loginAction = userState.createAction<{ username: string; email: string }>(
|
|
275
|
+
async (statePart, payload) => {
|
|
276
|
+
// Simulate API call
|
|
277
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
278
|
+
|
|
279
|
+
return {
|
|
280
|
+
isLoggedIn: true,
|
|
281
|
+
username: payload.username,
|
|
282
|
+
email: payload.email
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
// Subscribe to changes
|
|
288
|
+
userState.select(state => state.isLoggedIn).subscribe(isLoggedIn => {
|
|
289
|
+
console.log('Login status changed:', isLoggedIn);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// Dispatch actions
|
|
293
|
+
await loginAction.trigger({ username: 'john', email: 'john@example.com' });
|
|
294
|
+
```
|
|
206
295
|
|
|
207
|
-
|
|
296
|
+
### Key Features
|
|
208
297
|
|
|
209
|
-
|
|
210
|
-
- **Type-safe state management** with full TypeScript support
|
|
211
|
-
- **Reactive state updates** using RxJS observables
|
|
212
|
-
- **Persistent state** with IndexedDB storage
|
|
213
|
-
- **Performance optimized** with state hash detection
|
|
214
|
-
- **Modular architecture** with separate state parts
|
|
215
|
-
- **Action-based updates** for predictable state modifications
|
|
298
|
+
`@push.rocks/smartstate` provides a robust foundation for state management:
|
|
216
299
|
|
|
217
|
-
|
|
300
|
+
- **🎯 Type-safe** - Full TypeScript support with intelligent type inference
|
|
301
|
+
- **⚡ Performance optimized** - Async state hash detection prevents unnecessary re-renders
|
|
302
|
+
- **💾 Persistent state** - Built-in IndexedDB support for state persistence
|
|
303
|
+
- **🔄 Reactive** - Powered by RxJS for elegant async handling
|
|
304
|
+
- **🧩 Modular** - Organize state into logical, reusable parts
|
|
305
|
+
- **✅ Validated** - Built-in state validation with extensible validation logic
|
|
306
|
+
- **🎭 Flexible init modes** - Choose how state parts are initialized
|
|
307
|
+
- **📦 Zero config** - Works out of the box with sensible defaults
|
|
218
308
|
|
|
219
309
|
## License and Legal Information
|
|
220
310
|
|
|
@@ -13,9 +13,10 @@ export class Smartstate<StatePartNameType extends string> {
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Allows getting and initializing a new statepart
|
|
16
|
-
* initMode === 'soft'
|
|
17
|
-
* initMode === 'mandatory'
|
|
18
|
-
* initMode === 'force'
|
|
16
|
+
* initMode === 'soft' (default) - returns existing statepart if exists, creates new if not
|
|
17
|
+
* initMode === 'mandatory' - requires statepart to not exist, fails if it does
|
|
18
|
+
* initMode === 'force' - always creates new statepart, overwriting any existing
|
|
19
|
+
* initMode === 'persistent' - like 'soft' but with webstore persistence
|
|
19
20
|
* @param statePartNameArg
|
|
20
21
|
* @param initialArg
|
|
21
22
|
* @param initMode
|
|
@@ -23,19 +24,30 @@ export class Smartstate<StatePartNameType extends string> {
|
|
|
23
24
|
public async getStatePart<PayloadType>(
|
|
24
25
|
statePartNameArg: StatePartNameType,
|
|
25
26
|
initialArg?: PayloadType,
|
|
26
|
-
initMode
|
|
27
|
+
initMode: TInitMode = 'soft'
|
|
27
28
|
): Promise<StatePart<StatePartNameType, PayloadType>> {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
const existingStatePart = this.statePartMap[statePartNameArg];
|
|
30
|
+
|
|
31
|
+
if (existingStatePart) {
|
|
32
|
+
switch (initMode) {
|
|
33
|
+
case 'mandatory':
|
|
34
|
+
throw new Error(
|
|
35
|
+
`State part '${statePartNameArg}' already exists, but initMode is 'mandatory'`
|
|
36
|
+
);
|
|
37
|
+
case 'force':
|
|
38
|
+
// Force mode: create new state part
|
|
39
|
+
return this.createStatePart<PayloadType>(statePartNameArg, initialArg, initMode);
|
|
40
|
+
case 'soft':
|
|
41
|
+
case 'persistent':
|
|
42
|
+
default:
|
|
43
|
+
// Return existing state part
|
|
44
|
+
return existingStatePart as StatePart<StatePartNameType, PayloadType>;
|
|
33
45
|
}
|
|
34
|
-
return this.statePartMap[statePartNameArg] as StatePart<StatePartNameType, PayloadType>;
|
|
35
46
|
} else {
|
|
47
|
+
// State part doesn't exist
|
|
36
48
|
if (!initialArg) {
|
|
37
49
|
throw new Error(
|
|
38
|
-
|
|
50
|
+
`State part '${statePartNameArg}' does not exist and no initial state provided`
|
|
39
51
|
);
|
|
40
52
|
}
|
|
41
53
|
return this.createStatePart<PayloadType>(statePartNameArg, initialArg, initMode);
|
|
@@ -46,11 +58,12 @@ export class Smartstate<StatePartNameType extends string> {
|
|
|
46
58
|
* Creates a statepart
|
|
47
59
|
* @param statePartName
|
|
48
60
|
* @param initialPayloadArg
|
|
61
|
+
* @param initMode
|
|
49
62
|
*/
|
|
50
63
|
private async createStatePart<PayloadType>(
|
|
51
64
|
statePartName: StatePartNameType,
|
|
52
65
|
initialPayloadArg: PayloadType,
|
|
53
|
-
initMode
|
|
66
|
+
initMode: TInitMode = 'soft'
|
|
54
67
|
): Promise<StatePart<StatePartNameType, PayloadType>> {
|
|
55
68
|
const newState = new StatePart<StatePartNameType, PayloadType>(
|
|
56
69
|
statePartName,
|
|
@@ -64,8 +77,8 @@ export class Smartstate<StatePartNameType extends string> {
|
|
|
64
77
|
await newState.init();
|
|
65
78
|
const currentState = newState.getState();
|
|
66
79
|
await newState.setState({
|
|
67
|
-
...initialPayloadArg,
|
|
68
80
|
...currentState,
|
|
81
|
+
...initialPayloadArg,
|
|
69
82
|
});
|
|
70
83
|
this.statePartMap[statePartName] = newState;
|
|
71
84
|
return newState;
|