@push.rocks/smartstate 2.1.0 → 2.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.
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartstate',
6
- version: '2.1.0',
6
+ version: '2.1.1',
7
7
  description: 'A TypeScript-first reactive state management library with middleware, computed state, batching, persistence, and Web Component Context Protocol support.'
8
8
  };
9
9
  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx3QkFBd0I7SUFDOUIsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLDBKQUEwSjtDQUN4SyxDQUFBIn0=
@@ -10,6 +10,7 @@ export declare class Smartstate<StatePartNameType extends string> {
10
10
  };
11
11
  private pendingStatePartCreation;
12
12
  private batchDepth;
13
+ private isFlushing;
13
14
  private pendingNotifications;
14
15
  constructor();
15
16
  /**
@@ -10,6 +10,7 @@ export class Smartstate {
10
10
  this.pendingStatePartCreation = new Map();
11
11
  // Batch support
12
12
  this.batchDepth = 0;
13
+ this.isFlushing = false;
13
14
  this.pendingNotifications = new Set();
14
15
  }
15
16
  /**
@@ -34,11 +35,19 @@ export class Smartstate {
34
35
  }
35
36
  finally {
36
37
  this.batchDepth--;
37
- if (this.batchDepth === 0) {
38
- const pending = [...this.pendingNotifications];
39
- this.pendingNotifications.clear();
40
- for (const sp of pending) {
41
- await sp.notifyChange();
38
+ if (this.batchDepth === 0 && !this.isFlushing) {
39
+ this.isFlushing = true;
40
+ try {
41
+ while (this.pendingNotifications.size > 0) {
42
+ const pending = [...this.pendingNotifications];
43
+ this.pendingNotifications.clear();
44
+ for (const sp of pending) {
45
+ await sp.notifyChange();
46
+ }
47
+ }
48
+ }
49
+ finally {
50
+ this.isFlushing = false;
42
51
  }
43
52
  }
44
53
  }
@@ -64,6 +73,7 @@ export class Smartstate {
64
73
  case 'mandatory':
65
74
  throw new Error(`State part '${statePartNameArg}' already exists, but initMode is 'mandatory'`);
66
75
  case 'force':
76
+ existingStatePart.dispose();
67
77
  break;
68
78
  case 'soft':
69
79
  case 'persistent':
@@ -72,7 +82,7 @@ export class Smartstate {
72
82
  }
73
83
  }
74
84
  else {
75
- if (!initialArg) {
85
+ if (initialArg === undefined) {
76
86
  throw new Error(`State part '${statePartNameArg}' does not exist and no initial state provided`);
77
87
  }
78
88
  }
@@ -112,4 +122,4 @@ export class Smartstate {
112
122
  return newState;
113
123
  }
114
124
  }
115
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRzdGF0ZS5jbGFzc2VzLnNtYXJ0c3RhdGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydHN0YXRlLmNsYXNzZXMuc21hcnRzdGF0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHlCQUF5QixDQUFDO0FBQ25ELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUM5RCxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sa0NBQWtDLENBQUM7QUFJNUQ7O0dBRUc7QUFDSCxNQUFNLE9BQU8sVUFBVTtJQVNyQjtRQVJPLGlCQUFZLEdBQXVFLEVBQUUsQ0FBQztRQUVyRiw2QkFBd0IsR0FBNEQsSUFBSSxHQUFHLEVBQUUsQ0FBQztRQUV0RyxnQkFBZ0I7UUFDUixlQUFVLEdBQUcsQ0FBQyxDQUFDO1FBQ2YseUJBQW9CLEdBQUcsSUFBSSxHQUFHLEVBQXVCLENBQUM7SUFFL0MsQ0FBQztJQUVoQjs7T0FFRztJQUNILElBQVcsVUFBVTtRQUNuQixPQUFPLElBQUksQ0FBQyxVQUFVLEdBQUcsQ0FBQyxDQUFDO0lBQzdCLENBQUM7SUFFRDs7T0FFRztJQUNJLDJCQUEyQixDQUFDLFNBQThCO1FBQy9ELElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxHQUFHLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDM0MsQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLEtBQUssQ0FBQyxRQUFvQztRQUNyRCxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7UUFDbEIsSUFBSSxDQUFDO1lBQ0gsTUFBTSxRQUFRLEVBQUUsQ0FBQztRQUNuQixDQUFDO2dCQUFTLENBQUM7WUFDVCxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7WUFDbEIsSUFBSSxJQUFJLENBQUMsVUFBVSxLQUFLLENBQUMsRUFBRSxDQUFDO2dCQUMxQixNQUFNLE9BQU8sR0FBRyxDQUFDLEdBQUcsSUFBSSxDQUFDLG9CQUFvQixD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
125
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRzdGF0ZS5jbGFzc2VzLnNtYXJ0c3RhdGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydHN0YXRlLmNsYXNzZXMuc21hcnRzdGF0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHlCQUF5QixDQUFDO0FBQ25ELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUM5RCxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sa0NBQWtDLENBQUM7QUFJNUQ7O0dBRUc7QUFDSCxNQUFNLE9BQU8sVUFBVTtJQVVyQjtRQVRPLGlCQUFZLEdBQXVFLEVBQUUsQ0FBQztRQUVyRiw2QkFBd0IsR0FBNEQsSUFBSSxHQUFHLEVBQUUsQ0FBQztRQUV0RyxnQkFBZ0I7UUFDUixlQUFVLEdBQUcsQ0FBQyxDQUFDO1FBQ2YsZUFBVSxHQUFHLEtBQUssQ0FBQztRQUNuQix5QkFBb0IsR0FBRyxJQUFJLEdBQUcsRUFBdUIsQ0FBQztJQUUvQyxDQUFDO0lBRWhCOztPQUVHO0lBQ0gsSUFBVyxVQUFVO1FBQ25CLE9BQU8sSUFBSSxDQUFDLFVBQVUsR0FBRyxDQUFDLENBQUM7SUFDN0IsQ0FBQztJQUVEOztPQUVHO0lBQ0ksMkJBQTJCLENBQUMsU0FBOEI7UUFDL0QsSUFBSSxDQUFDLG9CQUFvQixDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUMzQyxDQUFDO0lBRUQ7O09BRUc7SUFDSSxLQUFLLENBQUMsS0FBSyxDQUFDLFFBQW9DO1FBQ3JELElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztRQUNsQixJQUFJLENBQUM7WUFDSCxNQUFNLFFBQVEsRUFBRSxDQUFDO1FBQ25CLENBQUM7Z0JBQVMsQ0FBQztZQUNULElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztZQUNsQixJQUFJLElBQUksQ0FBQyxVQUFVLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO2dCQUM5QyxJQUFJLENBQUMsVUFBVSxHQUFHLElBQUksQ0FBQztnQkFDdkIsSUFBSSxDQUFDO29CQUNILE9BQU8sSUFBSSxDQUFDLG9CQUFvQixDQUFDLElBQUksR0FBRyxDQUFDLEVBQUUsQ0FBQzt3QkFDMUMsTUFBTSxPQUFPLEdBQUcsQ0FBQyxHQUFHLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDO3dCQUMvQyxJQUFJLENBQUMsb0JBQW9CLENBQUMsS0FBSyxFQUFFLENBQUM7d0JBQ2xDLEtBQUssTUFBTSxFQUFFLElBQUksT0FBTyxFQUFFLENBQUM7NEJBQ3pCLE1BQU0sRUFBRSxDQUFDLFlBQVksRUFBRSxDQUFDO3dCQUMxQixDQUFDO29CQUNILENBQUM7Z0JBQ0gsQ0FBQzt3QkFBUyxDQUFDO29CQUNULElBQUksQ0FBQyxVQUFVLEdBQUcsS0FBSyxDQUFDO2dCQUMxQixDQUFDO1lBQ0gsQ0FBQztRQUNILENBQUM7SUFDSCxDQUFDO0lBRUQ7O09BRUc7SUFDSSxRQUFRLENBQ2IsT0FBNEMsRUFDNUMsU0FBd0M7UUFFeEMsT0FBTyxRQUFRLENBQUMsT0FBTyxFQUFFLFNBQVMsQ0FBQyxDQUFDO0lBQ3RDLENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxZQUFZLENBQ3ZCLGdCQUFtQyxFQUNuQyxVQUF3QixFQUN4QixXQUFzQixNQUFNO1FBRTVCLHlFQUF5RTtRQUN6RSxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsd0JBQXdCLENBQUMsR0FBRyxDQUFDLGdCQUFnQixDQUFDLENBQUM7UUFDcEUsSUFBSSxPQUFPLEVBQUUsQ0FBQztZQUNaLE9BQU8sT0FBNkQsQ0FBQztRQUN2RSxDQUFDO1FBRUQsTUFBTSxpQkFBaUIsR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDLGdCQUFnQixDQUFDLENBQUM7UUFFOUQsSUFBSSxpQkFBaUIsRUFBRSxDQUFDO1lBQ3RCLFFBQVEsUUFBUSxFQUFFLENBQUM7Z0JBQ2pCLEtBQUssV0FBVztvQkFDZCxNQUFNLElBQUksS0FBSyxDQUNiLGVBQWUsZ0JBQWdCLCtDQUErQyxDQUMvRSxDQUFDO2dCQUNKLEtBQUssT0FBTztvQkFDVixpQkFBaUIsQ0FBQyxPQUFPLEVBQUUsQ0FBQztvQkFDNUIsTUFBTTtnQkFDUixLQUFLLE1BQU0sQ0FBQztnQkFDWixLQUFLLFlBQVksQ0FBQztnQkFDbEI7b0JBQ0UsT0FBTyxpQkFBOEQsQ0FBQztZQUMxRSxDQUFDO1FBQ0gsQ0FBQzthQUFNLENBQUM7WUFDTixJQUFJLFVBQVUsS0FBSyxTQUFTLEVBQUUsQ0FBQztnQkFDN0IsTUFBTSxJQUFJLEtBQUssQ0FDYixlQUFlLGdCQUFnQixnREFBZ0QsQ0FDaEYsQ0FBQztZQUNKLENBQUM7UUFDSCxDQUFDO1FBRUQsTUFBTSxlQUFlLEdBQUcsSUFBSSxDQUFDLGVBQWUsQ0FBYyxnQkFBZ0IsRUFBRSxVQUFVLEVBQUUsUUFBUSxDQUFDLENBQUM7UUFDbEcsSUFBSSxDQUFDLHdCQUF3QixDQUFDLEdBQUcsQ0FBQyxnQkFBZ0IsRUFBRSxlQUFlLENBQUMsQ0FBQztRQUVyRSxJQUFJLENBQUM7WUFDSCxNQUFNLE1BQU0sR0FBRyxNQUFNLGVBQWUsQ0FBQztZQUNyQyxPQUFPLE1BQU0sQ0FBQztRQUNoQixDQUFDO2dCQUFTLENBQUM7WUFDVCxJQUFJLENBQUMsd0JBQXdCLENBQUMsTUFBTSxDQUFDLGdCQUFnQixDQUFDLENBQUM7UUFDekQsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNLLEtBQUssQ0FBQyxlQUFlLENBQzNCLGFBQWdDLEVBQ2hDLGlCQUE4QixFQUM5QixXQUFzQixNQUFNO1FBRTVCLE1BQU0sUUFBUSxHQUFHLElBQUksU0FBUyxDQUM1QixhQUFhLEVBQ2IsUUFBUSxLQUFLLFlBQVk7WUFDdkIsQ0FBQyxDQUFDO2dCQUNFLE1BQU0sRUFBRSxZQUFZO2dCQUNwQixTQUFTLEVBQUUsYUFBYTthQUN6QjtZQUNILENBQUMsQ0FBQyxJQUFJLENBQ1QsQ0FBQztRQUNGLFFBQVEsQ0FBQyxhQUFhLEdBQUcsSUFBSSxDQUFDO1FBQzlCLE1BQU0sUUFBUSxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ3RCLE1BQU0sWUFBWSxHQUFHLFFBQVEsQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUV6QyxJQUFJLFFBQVEsS0FBSyxZQUFZLElBQUksWUFBWSxLQUFLLFNBQVMsRUFBRSxDQUFDO1lBQzVELE1BQU0sUUFBUSxDQUFDLFFBQVEsQ0FBQztnQkFDdEIsR0FBRyxpQkFBaUI7Z0JBQ3BCLEdBQUcsWUFBWTthQUNoQixDQUFDLENBQUM7UUFDTCxDQUFDO2FBQU0sQ0FBQztZQUNOLE1BQU0sUUFBUSxDQUFDLFFBQVEsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO1FBQzdDLENBQUM7UUFFRCxJQUFJLENBQUMsWUFBWSxDQUFDLGFBQWEsQ0FBQyxHQUFHLFFBQVEsQ0FBQztRQUM1QyxPQUFPLFFBQVEsQ0FBQztJQUNsQixDQUFDO0NBQ0YifQ==
@@ -1,4 +1,3 @@
1
- import * as plugins from './smartstate.plugins.js';
2
1
  import { StatePart } from './smartstate.classes.statepart.js';
3
2
  /**
4
3
  * an actionmodifier for the state
@@ -12,4 +11,4 @@ export class StateAction {
12
11
  return this.statePartRef.dispatchAction(this, payload);
13
12
  }
14
13
  }
15
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRzdGF0ZS5jbGFzc2VzLnN0YXRlYWN0aW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc21hcnRzdGF0ZS5jbGFzc2VzLnN0YXRlYWN0aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0seUJBQXlCLENBQUM7QUFDbkQsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLG1DQUFtQyxDQUFDO0FBTTlEOztHQUVHO0FBQ0gsTUFBTSxPQUFPLFdBQVc7SUFDdEIsWUFDUyxZQUFpQyxFQUNqQyxTQUFxRDtRQURyRCxpQkFBWSxHQUFaLFlBQVksQ0FBcUI7UUFDakMsY0FBUyxHQUFULFNBQVMsQ0FBNEM7SUFDM0QsQ0FBQztJQUVHLE9BQU8sQ0FBQyxPQUEyQjtRQUN4QyxPQUFPLElBQUksQ0FBQyxZQUFZLENBQUMsY0FBYyxDQUFDLElBQUksRUFBRSxPQUFPLENBQUMsQ0FBQztJQUN6RCxDQUFDO0NBQ0YifQ==
14
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRzdGF0ZS5jbGFzc2VzLnN0YXRlYWN0aW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc21hcnRzdGF0ZS5jbGFzc2VzLnN0YXRlYWN0aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQU05RDs7R0FFRztBQUNILE1BQU0sT0FBTyxXQUFXO0lBQ3RCLFlBQ1MsWUFBaUMsRUFDakMsU0FBcUQ7UUFEckQsaUJBQVksR0FBWixZQUFZLENBQXFCO1FBQ2pDLGNBQVMsR0FBVCxTQUFTLENBQTRDO0lBQzNELENBQUM7SUFFRyxPQUFPLENBQUMsT0FBMkI7UUFDeEMsT0FBTyxJQUFJLENBQUMsWUFBWSxDQUFDLGNBQWMsQ0FBQyxJQUFJLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDekQsQ0FBQztDQUNGIn0=
@@ -8,8 +8,8 @@ export declare class StatePart<TStatePartName, TStatePayload> {
8
8
  stateStore: TStatePayload | undefined;
9
9
  smartstateRef?: Smartstate<any>;
10
10
  private cumulativeDeferred;
11
+ private mutationQueue;
11
12
  private pendingCumulativeNotification;
12
- private pendingBatchNotification;
13
13
  private webStoreOptions;
14
14
  private webStore;
15
15
  private middlewares;
@@ -31,9 +31,13 @@ export declare class StatePart<TStatePartName, TStatePayload> {
31
31
  */
32
32
  addMiddleware(middleware: TMiddleware<TStatePayload>): () => void;
33
33
  /**
34
- * sets the stateStore to the new state
34
+ * sets the stateStore to the new state (serialized via mutation queue)
35
35
  */
36
36
  setState(newStateArg: TStatePayload): Promise<TStatePayload>;
37
+ /**
38
+ * applies the state change (middleware → validate → persist → notify)
39
+ */
40
+ private applyState;
37
41
  /**
38
42
  * Validates state structure - can be overridden for custom validation
39
43
  */
@@ -75,4 +79,8 @@ export declare class StatePart<TStatePartName, TStatePayload> {
75
79
  * is executed
76
80
  */
77
81
  stateSetup(funcArg: (statePartArg?: StatePart<any, TStatePayload>) => Promise<TStatePayload>): Promise<void>;
82
+ /**
83
+ * disposes the state part, completing the Subject and cleaning up resources
84
+ */
85
+ dispose(): void;
78
86
  }
@@ -23,8 +23,8 @@ export class StatePart {
23
23
  constructor(nameArg, webStoreOptionsArg) {
24
24
  this.state = new plugins.smartrx.rxjs.Subject();
25
25
  this.cumulativeDeferred = plugins.smartpromise.cumulativeDefer();
26
+ this.mutationQueue = Promise.resolve();
26
27
  this.pendingCumulativeNotification = null;
27
- this.pendingBatchNotification = false;
28
28
  this.webStore = null;
29
29
  this.middlewares = [];
30
30
  // Selector memoization
@@ -70,9 +70,15 @@ export class StatePart {
70
70
  };
71
71
  }
72
72
  /**
73
- * sets the stateStore to the new state
73
+ * sets the stateStore to the new state (serialized via mutation queue)
74
74
  */
75
75
  async setState(newStateArg) {
76
+ return this.mutationQueue = this.mutationQueue.then(() => this.applyState(newStateArg), () => this.applyState(newStateArg));
77
+ }
78
+ /**
79
+ * applies the state change (middleware → validate → persist → notify)
80
+ */
81
+ async applyState(newStateArg) {
76
82
  // Run middleware chain
77
83
  let processedState = newStateArg;
78
84
  for (const mw of this.middlewares) {
@@ -101,27 +107,30 @@ export class StatePart {
101
107
  * notifies of a change on the state
102
108
  */
103
109
  async notifyChange() {
104
- if (!this.stateStore) {
110
+ const snapshot = this.stateStore;
111
+ if (snapshot === undefined) {
105
112
  return;
106
113
  }
107
114
  // If inside a batch, defer the notification
108
115
  if (this.smartstateRef?.isBatching) {
109
- this.pendingBatchNotification = true;
110
116
  this.smartstateRef.registerPendingNotification(this);
111
117
  return;
112
118
  }
113
119
  const createStateHash = async (stateArg) => {
114
120
  return await plugins.smarthashWeb.sha256FromString(plugins.smartjson.stableOneWayStringify(stateArg));
115
121
  };
116
- const currentHash = await createStateHash(this.stateStore);
117
- if (this.lastStateNotificationPayloadHash &&
118
- currentHash === this.lastStateNotificationPayloadHash) {
119
- return;
120
- }
121
- else {
122
+ try {
123
+ const currentHash = await createStateHash(snapshot);
124
+ if (this.lastStateNotificationPayloadHash &&
125
+ currentHash === this.lastStateNotificationPayloadHash) {
126
+ return;
127
+ }
122
128
  this.lastStateNotificationPayloadHash = currentHash;
123
129
  }
124
- this.state.next(this.stateStore);
130
+ catch (err) {
131
+ console.error(`State hash computation failed for '${this.name}':`, err);
132
+ }
133
+ this.state.next(snapshot);
125
134
  }
126
135
  /**
127
136
  * creates a cumulative notification by adding a change notification at the end of the call stack
@@ -130,10 +139,12 @@ export class StatePart {
130
139
  if (this.pendingCumulativeNotification) {
131
140
  clearTimeout(this.pendingCumulativeNotification);
132
141
  }
133
- this.pendingCumulativeNotification = setTimeout(async () => {
142
+ this.pendingCumulativeNotification = setTimeout(() => {
134
143
  this.pendingCumulativeNotification = null;
135
- if (this.stateStore) {
136
- await this.notifyChange();
144
+ if (this.stateStore !== undefined) {
145
+ this.notifyChange().catch((err) => {
146
+ console.error(`notifyChangeCumulative failed for '${this.name}':`, err);
147
+ });
137
148
  }
138
149
  }, 0);
139
150
  }
@@ -190,9 +201,13 @@ export class StatePart {
190
201
  */
191
202
  async dispatchAction(stateAction, actionPayload) {
192
203
  await this.cumulativeDeferred.promise;
193
- const newState = await stateAction.actionDef(this, actionPayload);
194
- await this.setState(newState);
195
- return this.getState();
204
+ return this.mutationQueue = this.mutationQueue.then(async () => {
205
+ const newState = await stateAction.actionDef(this, actionPayload);
206
+ return this.applyState(newState);
207
+ }, async () => {
208
+ const newState = await stateAction.actionDef(this, actionPayload);
209
+ return this.applyState(newState);
210
+ });
196
211
  }
197
212
  /**
198
213
  * waits until a certain part of the state becomes available.
@@ -217,7 +232,7 @@ export class StatePart {
217
232
  throw new Error('Aborted');
218
233
  }
219
234
  const subscription = selectedObservable.subscribe((value) => {
220
- if (value && !resolved) {
235
+ if (value !== undefined && value !== null && !resolved) {
221
236
  resolved = true;
222
237
  done.resolve(value);
223
238
  }
@@ -266,5 +281,20 @@ export class StatePart {
266
281
  this.cumulativeDeferred.addPromise(resultPromise);
267
282
  await this.setState(await resultPromise);
268
283
  }
284
+ /**
285
+ * disposes the state part, completing the Subject and cleaning up resources
286
+ */
287
+ dispose() {
288
+ this.state.complete();
289
+ if (this.pendingCumulativeNotification) {
290
+ clearTimeout(this.pendingCumulativeNotification);
291
+ this.pendingCumulativeNotification = null;
292
+ }
293
+ this.middlewares.length = 0;
294
+ this.selectorCache = new WeakMap();
295
+ this.defaultSelectObservable = null;
296
+ this.webStore = null;
297
+ this.smartstateRef = undefined;
298
+ }
269
299
  }
270
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRzdGF0ZS5jbGFzc2VzLnN0YXRlcGFydC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYXJ0c3RhdGUuY2xhc3Nlcy5zdGF0ZXBhcnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSx5QkFBeUIsQ0FBQztBQUNuRCxPQUFPLEVBQUUsVUFBVSxFQUFFLFdBQVcsRUFBRSxTQUFTLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDMUQsT0FBTyxFQUFFLFdBQVcsRUFBbUIsTUFBTSxxQ0FBcUMsQ0FBQztBQVFuRjs7R0FFRztBQUNILFNBQVMsZUFBZSxDQUFDLE1BQW1CO0lBQzFDLE9BQU8sSUFBSSxVQUFVLENBQU8sQ0FBQyxVQUFVLEVBQUUsRUFBRTtRQUN6QyxJQUFJLE1BQU0sQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNuQixVQUFVLENBQUMsSUFBSSxFQUFFLENBQUM7WUFDbEIsVUFBVSxDQUFDLFFBQVEsRUFBRSxDQUFDO1lBQ3RCLE9BQU87UUFDVCxDQUFDO1FBQ0QsTUFBTSxPQUFPLEdBQUcsR0FBRyxFQUFFO1lBQ25CLFVBQVUsQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUNsQixVQUFVLENBQUMsUUFBUSxFQUFFLENBQUM7UUFDeEIsQ0FBQyxDQUFDO1FBQ0YsTUFBTSxDQUFDLGdCQUFnQixDQUFDLE9BQU8sRUFBRSxPQUFPLENBQUMsQ0FBQztRQUMxQyxPQUFPLEdBQUcsRUFBRSxDQUFDLE1BQU0sQ0FBQyxtQkFBbUIsQ0FBQyxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDNUQsQ0FBQyxDQUFDLENBQUM7QUFDTCxDQUFDO0FBRUQsTUFBTSxPQUFPLFNBQVM7SUFtQnBCLFlBQVksT0FBdUIsRUFBRSxrQkFBc0Q7UUFqQnBGLFVBQUssR0FBRyxJQUFJLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBaUIsQ0FBQztRQUd6RCx1QkFBa0IsR0FBRyxPQUFPLENBQUMs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
300
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRzdGF0ZS5jbGFzc2VzLnN0YXRlcGFydC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYXJ0c3RhdGUuY2xhc3Nlcy5zdGF0ZXBhcnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSx5QkFBeUIsQ0FBQztBQUNuRCxPQUFPLEVBQUUsVUFBVSxFQUFFLFdBQVcsRUFBRSxTQUFTLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDMUQsT0FBTyxFQUFFLFdBQVcsRUFBbUIsTUFBTSxxQ0FBcUMsQ0FBQztBQVFuRjs7R0FFRztBQUNILFNBQVMsZUFBZSxDQUFDLE1BQW1CO0lBQzFDLE9BQU8sSUFBSSxVQUFVLENBQU8sQ0FBQyxVQUFVLEVBQUUsRUFBRTtRQUN6QyxJQUFJLE1BQU0sQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNuQixVQUFVLENBQUMsSUFBSSxFQUFFLENBQUM7WUFDbEIsVUFBVSxDQUFDLFFBQVEsRUFBRSxDQUFDO1lBQ3RCLE9BQU87UUFDVCxDQUFDO1FBQ0QsTUFBTSxPQUFPLEdBQUcsR0FBRyxFQUFFO1lBQ25CLFVBQVUsQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUNsQixVQUFVLENBQUMsUUFBUSxFQUFFLENBQUM7UUFDeEIsQ0FBQyxDQUFDO1FBQ0YsTUFBTSxDQUFDLGdCQUFnQixDQUFDLE9BQU8sRUFBRSxPQUFPLENBQUMsQ0FBQztRQUMxQyxPQUFPLEdBQUcsRUFBRSxDQUFDLE1BQU0sQ0FBQyxtQkFBbUIsQ0FBQyxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDNUQsQ0FBQyxDQUFDLENBQUM7QUFDTCxDQUFDO0FBRUQsTUFBTSxPQUFPLFNBQVM7SUFtQnBCLFlBQVksT0FBdUIsRUFBRSxrQkFBc0Q7UUFqQnBGLFVBQUssR0FBRyxJQUFJLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBaUIsQ0FBQztRQUd6RCx1QkFBa0IsR0FBRyxPQUFPLENBQUMs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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@push.rocks/smartstate",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
4
4
  "private": false,
5
5
  "description": "A TypeScript-first reactive state management library with middleware, computed state, batching, persistence, and Web Component Context Protocol support.",
6
6
  "main": "dist_ts/index.js",
package/readme.md CHANGED
@@ -46,9 +46,9 @@ userState.select((s) => s.name).subscribe((name) => {
46
46
  await userState.setState({ name: 'Alice', loggedIn: true });
47
47
  ```
48
48
 
49
- ### State Parts & Init Modes
49
+ ### 🧩 State Parts & Init Modes
50
50
 
51
- State parts are isolated, typed units of state. Create them with `getStatePart()`:
51
+ State parts are isolated, typed units of state. They are the building blocks of your application's state tree. Create them via `getStatePart()`:
52
52
 
53
53
  ```typescript
54
54
  const part = await state.getStatePart<IMyState>(name, initialState, initMode);
@@ -57,68 +57,95 @@ const part = await state.getStatePart<IMyState>(name, initialState, initMode);
57
57
  | Init Mode | Behavior |
58
58
  |-----------|----------|
59
59
  | `'soft'` (default) | Returns existing if found, creates new otherwise |
60
- | `'mandatory'` | Throws if state part already exists |
61
- | `'force'` | Always creates new, overwrites existing |
62
- | `'persistent'` | Like `'soft'` but persists to IndexedDB via WebStore |
60
+ | `'mandatory'` | Throws if state part already exists — useful for ensuring single-initialization |
61
+ | `'force'` | Always creates a new state part, overwriting any existing one |
62
+ | `'persistent'` | Like `'soft'` but automatically persists state to IndexedDB via WebStore |
63
63
 
64
- #### Persistent State
64
+ You can use either enums or string literal types for state part names:
65
65
 
66
66
  ```typescript
67
- const settings = await state.getStatePart('settings', { theme: 'dark' }, 'persistent');
68
- // Automatically saved to IndexedDB. On next app load, persisted values override defaults.
67
+ // String literal types (simpler)
68
+ type AppParts = 'user' | 'settings' | 'cart';
69
+
70
+ // Enums (more explicit)
71
+ enum AppParts {
72
+ User = 'user',
73
+ Settings = 'settings',
74
+ Cart = 'cart',
75
+ }
76
+ ```
77
+
78
+ #### 💾 Persistent State
79
+
80
+ ```typescript
81
+ const settings = await state.getStatePart('settings', { theme: 'dark', fontSize: 14 }, 'persistent');
82
+
83
+ // ✅ Automatically saved to IndexedDB on every setState()
84
+ // ✅ On next app load, persisted values override defaults
85
+ // ✅ Persistence writes complete before in-memory updates (atomic)
69
86
  ```
70
87
 
71
- ### Selecting State
88
+ ### 🔭 Selecting State
72
89
 
73
- `select()` returns an RxJS Observable that emits the current value immediately and on every change:
90
+ `select()` returns an RxJS Observable that emits the current value immediately and on every subsequent change:
74
91
 
75
92
  ```typescript
76
93
  // Full state
77
94
  userState.select().subscribe((state) => console.log(state));
78
95
 
79
- // Derived value via selector
96
+ // Derived value via selector function
80
97
  userState.select((s) => s.name).subscribe((name) => console.log(name));
81
98
  ```
82
99
 
83
- Selectors are **memoized** — calling `select(fn)` with the same function reference returns the same cached Observable, shared across all subscribers.
100
+ Selectors are **memoized** — calling `select(fn)` with the same function reference returns the same cached Observable, shared across all subscribers via `shareReplay`. This means you can call `select(mySelector)` in multiple places without creating duplicate subscriptions.
84
101
 
85
- #### AbortSignal Support
102
+ #### ✂️ AbortSignal Support
86
103
 
87
- Clean up subscriptions without manual `unsubscribe()`:
104
+ Clean up subscriptions without manual `.unsubscribe()` — the modern way:
88
105
 
89
106
  ```typescript
90
107
  const controller = new AbortController();
91
108
 
92
109
  userState.select((s) => s.name, { signal: controller.signal }).subscribe((name) => {
93
- console.log(name); // stops receiving when aborted
110
+ console.log(name); // automatically stops receiving when aborted
94
111
  });
95
112
 
96
- // Later: clean up
113
+ // Later: clean up all subscriptions tied to this signal
97
114
  controller.abort();
98
115
  ```
99
116
 
100
- ### Actions
117
+ ### Actions
101
118
 
102
- Actions provide controlled, named state mutations:
119
+ Actions provide controlled, named state mutations with full async support:
103
120
 
104
121
  ```typescript
105
- const login = userState.createAction<{ name: string }>(async (statePart, payload) => {
106
- return { ...statePart.getState(), name: payload.name, loggedIn: true };
122
+ interface ILoginPayload {
123
+ username: string;
124
+ email: string;
125
+ }
126
+
127
+ const loginAction = userState.createAction<ILoginPayload>(async (statePart, payload) => {
128
+ // You have access to the current state via statePart.getState()
129
+ const current = statePart.getState();
130
+ return { ...current, name: payload.username, loggedIn: true };
107
131
  });
108
132
 
109
133
  // Two equivalent ways to dispatch:
110
- await login.trigger({ name: 'Alice' });
111
- await userState.dispatchAction(login, { name: 'Alice' });
134
+ await loginAction.trigger({ username: 'Alice', email: 'alice@example.com' });
135
+ // or
136
+ await userState.dispatchAction(loginAction, { username: 'Alice', email: 'alice@example.com' });
112
137
  ```
113
138
 
114
- ### Middleware
139
+ Both `trigger()` and `dispatchAction()` return a Promise with the new state.
115
140
 
116
- Intercept every `setState()` call to transform, validate, or reject state changes:
141
+ ### 🛡️ Middleware
142
+
143
+ Intercept every `setState()` call to transform, validate, log, or reject state changes:
117
144
 
118
145
  ```typescript
119
146
  // Logging middleware
120
147
  userState.addMiddleware((newState, oldState) => {
121
- console.log('State changing from', oldState, 'to', newState);
148
+ console.log('State changing:', oldState, '', newState);
122
149
  return newState;
123
150
  });
124
151
 
@@ -133,16 +160,22 @@ userState.addMiddleware((newState) => {
133
160
  return { ...newState, name: newState.name.trim() };
134
161
  });
135
162
 
136
- // Removal — addMiddleware returns a dispose function
163
+ // Async middleware
164
+ userState.addMiddleware(async (newState, oldState) => {
165
+ await auditLog('state-change', { from: oldState, to: newState });
166
+ return newState;
167
+ });
168
+
169
+ // Removal — addMiddleware() returns a dispose function
137
170
  const remove = userState.addMiddleware(myMiddleware);
138
171
  remove(); // middleware no longer runs
139
172
  ```
140
173
 
141
- Middleware runs sequentially in insertion order. If any middleware throws, the state is unchanged (atomic).
174
+ Middleware runs **sequentially** in insertion order. If any middleware throws, the state remains unchanged — the operation is **atomic**.
142
175
 
143
- ### Computed / Derived State
176
+ ### 🧮 Computed / Derived State
144
177
 
145
- Derive reactive values from one or more state parts:
178
+ Derive reactive values from one or more state parts using `combineLatest` under the hood:
146
179
 
147
180
  ```typescript
148
181
  import { computed } from '@push.rocks/smartstate';
@@ -159,42 +192,44 @@ const greeting$ = computed(
159
192
  greeting$.subscribe((msg) => console.log(msg));
160
193
  // => "Hello, Jane (en)"
161
194
 
162
- // Also available as a method on Smartstate:
163
- const greeting2$ = state.computed([userState, settingsState], (user, settings) => /* ... */);
195
+ // Also available as a convenience method on the Smartstate instance:
196
+ const greeting2$ = state.computed(
197
+ [userState, settingsState],
198
+ (user, settings) => `${user.firstName} - ${settings.locale}`,
199
+ );
164
200
  ```
165
201
 
166
- Computed observables are **lazy** — they only subscribe to sources when someone subscribes to them.
202
+ Computed observables are **lazy** — they only subscribe to their sources when someone subscribes to them, and they automatically unsubscribe when all subscribers disconnect.
167
203
 
168
- ### Batch Updates
204
+ ### 📦 Batch Updates
169
205
 
170
- Update multiple state parts without intermediate notifications:
206
+ Update multiple state parts at once while deferring all notifications until the entire batch completes:
171
207
 
172
208
  ```typescript
173
209
  const partA = await state.getStatePart('a', { value: 1 });
174
210
  const partB = await state.getStatePart('b', { value: 2 });
175
211
 
176
- // Subscribers see no updates during the batch — only after it completes
177
212
  await state.batch(async () => {
178
213
  await partA.setState({ value: 10 });
179
214
  await partB.setState({ value: 20 });
180
- // Notifications are deferred here
215
+ // No notifications fire inside the batch
181
216
  });
182
- // Both subscribers now fire with their new values
217
+ // Both subscribers now fire with their new values simultaneously
183
218
 
184
- // Nested batches are supported — flush happens at the outermost level
219
+ // Nested batches are supported — flush happens at the outermost level only
185
220
  await state.batch(async () => {
186
221
  await partA.setState({ value: 100 });
187
222
  await state.batch(async () => {
188
223
  await partB.setState({ value: 200 });
189
224
  });
190
- // Still deferred
225
+ // Still deferred — inner batch doesn't trigger flush
191
226
  });
192
227
  // Now both fire
193
228
  ```
194
229
 
195
- ### Waiting for State
230
+ ### Waiting for State
196
231
 
197
- Wait for a specific state condition to be met:
232
+ Wait for a specific state condition to be met before proceeding:
198
233
 
199
234
  ```typescript
200
235
  // Wait for any truthy state
@@ -203,10 +238,10 @@ const currentState = await userState.waitUntilPresent();
203
238
  // Wait for a specific condition
204
239
  const name = await userState.waitUntilPresent((s) => s.name || undefined);
205
240
 
206
- // With timeout (backward compatible)
241
+ // With timeout (milliseconds)
207
242
  const name = await userState.waitUntilPresent((s) => s.name || undefined, 5000);
208
243
 
209
- // With AbortSignal
244
+ // With AbortSignal and/or timeout via options object
210
245
  const controller = new AbortController();
211
246
  try {
212
247
  const name = await userState.waitUntilPresent(
@@ -214,109 +249,142 @@ try {
214
249
  { timeoutMs: 5000, signal: controller.signal },
215
250
  );
216
251
  } catch (e) {
217
- // 'Aborted' or timeout error
252
+ // e.message is 'Aborted' or 'waitUntilPresent timed out after 5000ms'
218
253
  }
219
254
  ```
220
255
 
221
- ### Context Protocol Bridge (Web Components)
256
+ ### 🌐 Context Protocol Bridge (Web Components)
222
257
 
223
- Expose state parts to web components via the [W3C Context Protocol](https://github.com/webcomponents-cg/community-protocols/blob/main/proposals/context.md):
258
+ Expose state parts to web components via the [W3C Context Protocol](https://github.com/webcomponents-cg/community-protocols/blob/main/proposals/context.md). This lets any web component framework (Lit, FAST, Stencil, or vanilla) consume your state without coupling:
224
259
 
225
260
  ```typescript
226
261
  import { attachContextProvider } from '@push.rocks/smartstate';
227
262
 
228
- // Define a context key
263
+ // Define a context key (use Symbol for uniqueness)
229
264
  const themeContext = Symbol('theme');
230
265
 
231
- // Attach a provider to a DOM element
232
- const cleanup = attachContextProvider(myElement, {
266
+ // Attach a provider to a DOM element — any descendant can consume it
267
+ const cleanup = attachContextProvider(document.body, {
233
268
  context: themeContext,
234
269
  statePart: settingsState,
235
- selectorFn: (s) => s.theme, // optional: provide derived value
270
+ selectorFn: (s) => s.theme, // optional: provide a derived value instead of full state
236
271
  });
237
272
 
238
- // Any descendant can request this context:
239
- myElement.dispatchEvent(
273
+ // A consumer dispatches a context-request event:
274
+ myComponent.dispatchEvent(
240
275
  new CustomEvent('context-request', {
241
276
  bubbles: true,
242
277
  composed: true,
243
278
  detail: {
244
279
  context: themeContext,
245
- callback: (theme) => console.log('Theme:', theme),
246
- subscribe: true, // receive updates on state changes
280
+ callback: (theme) => console.log('Got theme:', theme),
281
+ subscribe: true, // receive updates whenever the state changes
247
282
  },
248
283
  }),
249
284
  );
250
285
 
251
- // Cleanup when done
286
+ // Works seamlessly with Lit's @consume() decorator, FAST's context, etc.
287
+
288
+ // Cleanup when the provider is no longer needed
252
289
  cleanup();
253
290
  ```
254
291
 
255
- This works with Lit's `@consume()` decorator, FAST, or any framework implementing the Context Protocol.
292
+ ### State Validation
256
293
 
257
- ### State Validation
258
-
259
- Built-in null/undefined validation. Extend for custom rules:
294
+ Built-in validation prevents `null` and `undefined` from being set as state. For custom validation, extend `StatePart`:
260
295
 
261
296
  ```typescript
262
- class ValidatedPart<T> extends StatePart<string, T> {
263
- protected validateState(stateArg: any): stateArg is T {
264
- return super.validateState(stateArg) && typeof stateArg.name === 'string';
297
+ import { StatePart } from '@push.rocks/smartstate';
298
+
299
+ class ValidatedUserPart extends StatePart<string, IUserState> {
300
+ protected validateState(stateArg: any): stateArg is IUserState {
301
+ return (
302
+ super.validateState(stateArg) &&
303
+ typeof stateArg.name === 'string' &&
304
+ typeof stateArg.loggedIn === 'boolean'
305
+ );
265
306
  }
266
307
  }
267
308
  ```
268
309
 
269
- ### Performance Features
310
+ If validation fails, `setState()` throws and the state remains unchanged.
311
+
312
+ ### ⚙️ Async State Setup
313
+
314
+ Initialize state with async operations while ensuring actions wait for setup to complete:
315
+
316
+ ```typescript
317
+ await userState.stateSetup(async (statePart) => {
318
+ const userData = await fetchUserFromAPI();
319
+ return { ...statePart.getState(), ...userData };
320
+ });
321
+
322
+ // Any dispatchAction() calls will automatically wait for stateSetup() to finish
323
+ ```
324
+
325
+ ### 🏎️ Performance
270
326
 
271
- - **SHA256 Change Detection** — identical state values don't trigger notifications, even with different object references
272
- - **Selector Memoization** — `select(fn)` caches observables by function reference, sharing one upstream subscription across all subscribers
273
- - **Cumulative Notifications** — `notifyChangeCumulative()` debounces rapid changes into a single notification
274
- - **Concurrent Safety** — simultaneous `getStatePart()` calls for the same name return the same promise, preventing duplicate creation
275
- - **Atomic Persistence** — WebStore writes complete before in-memory state updates, ensuring consistency
276
- - **Batch Deferred Notifications** — `batch()` suppresses all notifications until the batch completes
327
+ Smartstate is built with performance in mind:
328
+
329
+ - **🔒 SHA256 Change Detection** — Uses content hashing to detect actual changes. Identical state values don't trigger notifications, even with different object references.
330
+ - **♻️ Selector Memoization** — `select(fn)` caches observables by function reference and shares them via `shareReplay({ refCount: true })`. Multiple subscribers share one upstream subscription.
331
+ - **📦 Cumulative Notifications** — `notifyChangeCumulative()` debounces rapid changes into a single notification at the end of the call stack.
332
+ - **🔐 Concurrent Safety** — Simultaneous `getStatePart()` calls for the same name return the same promise, preventing duplicate creation or race conditions.
333
+ - **💾 Atomic Persistence** — WebStore writes complete before in-memory state updates, ensuring consistency even if the process crashes mid-write.
334
+ - **⏸️ Batch Deferred Notifications** — `batch()` suppresses all subscriber notifications until every update in the batch completes.
277
335
 
278
336
  ## API Reference
279
337
 
280
338
  ### `Smartstate<T>`
281
339
 
282
- | Method | Description |
283
- |--------|-------------|
284
- | `getStatePart(name, initial?, initMode?)` | Get or create a state part |
285
- | `batch(fn)` | Batch updates, defer notifications |
286
- | `computed(sources, fn)` | Create computed observable |
287
- | `isBatching` | Whether a batch is active |
340
+ | Method / Property | Description |
341
+ |-------------------|-------------|
342
+ | `getStatePart(name, initial?, initMode?)` | Get or create a typed state part |
343
+ | `batch(fn)` | Batch state updates, defer all notifications until complete |
344
+ | `computed(sources, fn)` | Create a computed observable from multiple state parts |
345
+ | `isBatching` | `boolean` — whether a batch is currently active |
346
+ | `statePartMap` | Registry of all created state parts |
288
347
 
289
348
  ### `StatePart<TName, TPayload>`
290
349
 
291
350
  | Method | Description |
292
351
  |--------|-------------|
293
- | `getState()` | Get current state (or undefined) |
294
- | `setState(newState)` | Set state (runs middleware, validates, persists, notifies) |
295
- | `select(selectorFn?, options?)` | Subscribe to state changes |
296
- | `createAction(actionDef)` | Create a named action |
297
- | `dispatchAction(action, payload)` | Dispatch an action |
298
- | `addMiddleware(fn)` | Add middleware, returns removal function |
299
- | `waitUntilPresent(selectorFn?, options?)` | Wait for state condition |
300
- | `notifyChange()` | Manually trigger notification |
301
- | `notifyChangeCumulative()` | Debounced notification |
302
- | `stateSetup(fn)` | Async state initialization |
352
+ | `getState()` | Get current state (returns `TPayload \| undefined`) |
353
+ | `setState(newState)` | Set state runs middleware validates persists notifies |
354
+ | `select(selectorFn?, options?)` | Returns an Observable of state or derived values. Options: `{ signal?: AbortSignal }` |
355
+ | `createAction(actionDef)` | Create a reusable, typed state action |
356
+ | `dispatchAction(action, payload)` | Dispatch an action and return the new state |
357
+ | `addMiddleware(fn)` | Add a middleware interceptor. Returns a removal function |
358
+ | `waitUntilPresent(selectorFn?, opts?)` | Wait for a state condition. Opts: `number` (timeout) or `{ timeoutMs?, signal? }` |
359
+ | `notifyChange()` | Manually trigger a change notification (with hash dedup) |
360
+ | `notifyChangeCumulative()` | Debounced notification — fires at end of call stack |
361
+ | `stateSetup(fn)` | Async state initialization with action serialization |
303
362
 
304
363
  ### `StateAction<TState, TPayload>`
305
364
 
306
365
  | Method | Description |
307
366
  |--------|-------------|
308
- | `trigger(payload)` | Dispatch the action |
367
+ | `trigger(payload)` | Dispatch the action on its associated state part |
309
368
 
310
369
  ### Standalone Functions
311
370
 
312
371
  | Function | Description |
313
372
  |----------|-------------|
314
- | `computed(sources, fn)` | Create computed observable from state parts |
315
- | `attachContextProvider(element, options)` | Bridge state to Context Protocol |
373
+ | `computed(sources, fn)` | Create a computed observable from multiple state parts |
374
+ | `attachContextProvider(element, options)` | Bridge a state part to the W3C Context Protocol |
375
+
376
+ ### Exported Types
377
+
378
+ | Type | Description |
379
+ |------|-------------|
380
+ | `TInitMode` | `'soft' \| 'mandatory' \| 'force' \| 'persistent'` |
381
+ | `TMiddleware<TPayload>` | `(newState, oldState) => TPayload \| Promise<TPayload>` |
382
+ | `IActionDef<TState, TPayload>` | Action definition function signature |
383
+ | `IContextProviderOptions<TPayload>` | Options for `attachContextProvider` |
316
384
 
317
385
  ## License and Legal Information
318
386
 
319
- This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [license](./license) file.
387
+ This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [LICENSE](./LICENSE) file.
320
388
 
321
389
  **Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
322
390
 
@@ -328,7 +396,7 @@ Use of these trademarks must comply with Task Venture Capital GmbH's Trademark G
328
396
 
329
397
  ### Company Information
330
398
 
331
- Task Venture Capital GmbH
399
+ Task Venture Capital GmbH
332
400
  Registered at District Court Bremen HRB 35230 HB, Germany
333
401
 
334
402
  For any legal inquiries or further information, please contact us via email at hello@task.vc.
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartstate',
6
- version: '2.1.0',
6
+ version: '2.1.1',
7
7
  description: 'A TypeScript-first reactive state management library with middleware, computed state, batching, persistence, and Web Component Context Protocol support.'
8
8
  }
@@ -14,6 +14,7 @@ export class Smartstate<StatePartNameType extends string> {
14
14
 
15
15
  // Batch support
16
16
  private batchDepth = 0;
17
+ private isFlushing = false;
17
18
  private pendingNotifications = new Set<StatePart<any, any>>();
18
19
 
19
20
  constructor() {}
@@ -41,11 +42,18 @@ export class Smartstate<StatePartNameType extends string> {
41
42
  await updateFn();
42
43
  } finally {
43
44
  this.batchDepth--;
44
- if (this.batchDepth === 0) {
45
- const pending = [...this.pendingNotifications];
46
- this.pendingNotifications.clear();
47
- for (const sp of pending) {
48
- await sp.notifyChange();
45
+ if (this.batchDepth === 0 && !this.isFlushing) {
46
+ this.isFlushing = true;
47
+ try {
48
+ while (this.pendingNotifications.size > 0) {
49
+ const pending = [...this.pendingNotifications];
50
+ this.pendingNotifications.clear();
51
+ for (const sp of pending) {
52
+ await sp.notifyChange();
53
+ }
54
+ }
55
+ } finally {
56
+ this.isFlushing = false;
49
57
  }
50
58
  }
51
59
  }
@@ -84,6 +92,7 @@ export class Smartstate<StatePartNameType extends string> {
84
92
  `State part '${statePartNameArg}' already exists, but initMode is 'mandatory'`
85
93
  );
86
94
  case 'force':
95
+ existingStatePart.dispose();
87
96
  break;
88
97
  case 'soft':
89
98
  case 'persistent':
@@ -91,7 +100,7 @@ export class Smartstate<StatePartNameType extends string> {
91
100
  return existingStatePart as StatePart<StatePartNameType, PayloadType>;
92
101
  }
93
102
  } else {
94
- if (!initialArg) {
103
+ if (initialArg === undefined) {
95
104
  throw new Error(
96
105
  `State part '${statePartNameArg}' does not exist and no initial state provided`
97
106
  );
@@ -1,4 +1,3 @@
1
- import * as plugins from './smartstate.plugins.js';
2
1
  import { StatePart } from './smartstate.classes.statepart.js';
3
2
 
4
3
  export interface IActionDef<TStateType, TActionPayloadType> {
@@ -34,8 +34,8 @@ export class StatePart<TStatePartName, TStatePayload> {
34
34
  public smartstateRef?: Smartstate<any>;
35
35
  private cumulativeDeferred = plugins.smartpromise.cumulativeDefer();
36
36
 
37
+ private mutationQueue: Promise<any> = Promise.resolve();
37
38
  private pendingCumulativeNotification: ReturnType<typeof setTimeout> | null = null;
38
- private pendingBatchNotification = false;
39
39
 
40
40
  private webStoreOptions: plugins.webstore.IWebStoreOptions;
41
41
  private webStore: plugins.webstore.WebStore<TStatePayload> | null = null;
@@ -92,9 +92,19 @@ export class StatePart<TStatePartName, TStatePayload> {
92
92
  }
93
93
 
94
94
  /**
95
- * sets the stateStore to the new state
95
+ * sets the stateStore to the new state (serialized via mutation queue)
96
96
  */
97
- public async setState(newStateArg: TStatePayload) {
97
+ public async setState(newStateArg: TStatePayload): Promise<TStatePayload> {
98
+ return this.mutationQueue = this.mutationQueue.then(
99
+ () => this.applyState(newStateArg),
100
+ () => this.applyState(newStateArg),
101
+ );
102
+ }
103
+
104
+ /**
105
+ * applies the state change (middleware → validate → persist → notify)
106
+ */
107
+ private async applyState(newStateArg: TStatePayload): Promise<TStatePayload> {
98
108
  // Run middleware chain
99
109
  let processedState = newStateArg;
100
110
  for (const mw of this.middlewares) {
@@ -129,13 +139,13 @@ export class StatePart<TStatePartName, TStatePayload> {
129
139
  * notifies of a change on the state
130
140
  */
131
141
  public async notifyChange() {
132
- if (!this.stateStore) {
142
+ const snapshot = this.stateStore;
143
+ if (snapshot === undefined) {
133
144
  return;
134
145
  }
135
146
 
136
147
  // If inside a batch, defer the notification
137
148
  if (this.smartstateRef?.isBatching) {
138
- this.pendingBatchNotification = true;
139
149
  this.smartstateRef.registerPendingNotification(this);
140
150
  return;
141
151
  }
@@ -143,16 +153,19 @@ export class StatePart<TStatePartName, TStatePayload> {
143
153
  const createStateHash = async (stateArg: any) => {
144
154
  return await plugins.smarthashWeb.sha256FromString(plugins.smartjson.stableOneWayStringify(stateArg));
145
155
  };
146
- const currentHash = await createStateHash(this.stateStore);
147
- if (
148
- this.lastStateNotificationPayloadHash &&
149
- currentHash === this.lastStateNotificationPayloadHash
150
- ) {
151
- return;
152
- } else {
156
+ try {
157
+ const currentHash = await createStateHash(snapshot);
158
+ if (
159
+ this.lastStateNotificationPayloadHash &&
160
+ currentHash === this.lastStateNotificationPayloadHash
161
+ ) {
162
+ return;
163
+ }
153
164
  this.lastStateNotificationPayloadHash = currentHash;
165
+ } catch (err) {
166
+ console.error(`State hash computation failed for '${this.name}':`, err);
154
167
  }
155
- this.state.next(this.stateStore);
168
+ this.state.next(snapshot);
156
169
  }
157
170
  private lastStateNotificationPayloadHash: any;
158
171
 
@@ -164,10 +177,12 @@ export class StatePart<TStatePartName, TStatePayload> {
164
177
  clearTimeout(this.pendingCumulativeNotification);
165
178
  }
166
179
 
167
- this.pendingCumulativeNotification = setTimeout(async () => {
180
+ this.pendingCumulativeNotification = setTimeout(() => {
168
181
  this.pendingCumulativeNotification = null;
169
- if (this.stateStore) {
170
- await this.notifyChange();
182
+ if (this.stateStore !== undefined) {
183
+ this.notifyChange().catch((err) => {
184
+ console.error(`notifyChangeCumulative failed for '${this.name}':`, err);
185
+ });
171
186
  }
172
187
  }, 0);
173
188
  }
@@ -239,9 +254,16 @@ export class StatePart<TStatePartName, TStatePayload> {
239
254
  */
240
255
  public async dispatchAction<T>(stateAction: StateAction<TStatePayload, T>, actionPayload: T): Promise<TStatePayload> {
241
256
  await this.cumulativeDeferred.promise;
242
- const newState = await stateAction.actionDef(this, actionPayload);
243
- await this.setState(newState);
244
- return this.getState();
257
+ return this.mutationQueue = this.mutationQueue.then(
258
+ async () => {
259
+ const newState = await stateAction.actionDef(this, actionPayload);
260
+ return this.applyState(newState);
261
+ },
262
+ async () => {
263
+ const newState = await stateAction.actionDef(this, actionPayload);
264
+ return this.applyState(newState);
265
+ },
266
+ );
245
267
  }
246
268
 
247
269
  /**
@@ -272,7 +294,7 @@ export class StatePart<TStatePartName, TStatePayload> {
272
294
  }
273
295
 
274
296
  const subscription = selectedObservable.subscribe((value) => {
275
- if (value && !resolved) {
297
+ if (value !== undefined && value !== null && !resolved) {
276
298
  resolved = true;
277
299
  done.resolve(value);
278
300
  }
@@ -325,4 +347,20 @@ export class StatePart<TStatePartName, TStatePayload> {
325
347
  this.cumulativeDeferred.addPromise(resultPromise);
326
348
  await this.setState(await resultPromise);
327
349
  }
350
+
351
+ /**
352
+ * disposes the state part, completing the Subject and cleaning up resources
353
+ */
354
+ public dispose(): void {
355
+ this.state.complete();
356
+ if (this.pendingCumulativeNotification) {
357
+ clearTimeout(this.pendingCumulativeNotification);
358
+ this.pendingCumulativeNotification = null;
359
+ }
360
+ this.middlewares.length = 0;
361
+ this.selectorCache = new WeakMap();
362
+ this.defaultSelectObservable = null;
363
+ this.webStore = null;
364
+ this.smartstateRef = undefined;
365
+ }
328
366
  }