@push.rocks/smartstate 2.0.27 → 2.0.31

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.0.27',
6
+ version: '2.0.31',
7
7
  description: 'A package for handling and managing state in applications.'
8
8
  };
9
9
  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx3QkFBd0I7SUFDOUIsT0FBTyxFQUFFLFFBQVE7SUFDakIsV0FBVyxFQUFFLDREQUE0RDtDQUMxRSxDQUFBIn0=
@@ -7,6 +7,7 @@ export declare class Smartstate<StatePartNameType extends string> {
7
7
  statePartMap: {
8
8
  [key in StatePartNameType]?: StatePart<StatePartNameType, any>;
9
9
  };
10
+ private pendingStatePartCreation;
10
11
  constructor();
11
12
  /**
12
13
  * Allows getting and initializing a new statepart
@@ -6,6 +6,7 @@ import { StatePart } from './smartstate.classes.statepart.js';
6
6
  export class Smartstate {
7
7
  constructor() {
8
8
  this.statePartMap = {};
9
+ this.pendingStatePartCreation = new Map();
9
10
  }
10
11
  /**
11
12
  * Allows getting and initializing a new statepart
@@ -18,6 +19,11 @@ export class Smartstate {
18
19
  * @param initMode
19
20
  */
20
21
  async getStatePart(statePartNameArg, initialArg, initMode = 'soft') {
22
+ // Return pending creation if one exists to prevent duplicate state parts
23
+ const pending = this.pendingStatePartCreation.get(statePartNameArg);
24
+ if (pending) {
25
+ return pending;
26
+ }
21
27
  const existingStatePart = this.statePartMap[statePartNameArg];
22
28
  if (existingStatePart) {
23
29
  switch (initMode) {
@@ -25,7 +31,7 @@ export class Smartstate {
25
31
  throw new Error(`State part '${statePartNameArg}' already exists, but initMode is 'mandatory'`);
26
32
  case 'force':
27
33
  // Force mode: create new state part
28
- return this.createStatePart(statePartNameArg, initialArg, initMode);
34
+ break; // Fall through to creation
29
35
  case 'soft':
30
36
  case 'persistent':
31
37
  default:
@@ -38,7 +44,15 @@ export class Smartstate {
38
44
  if (!initialArg) {
39
45
  throw new Error(`State part '${statePartNameArg}' does not exist and no initial state provided`);
40
46
  }
41
- return this.createStatePart(statePartNameArg, initialArg, initMode);
47
+ }
48
+ const creationPromise = this.createStatePart(statePartNameArg, initialArg, initMode);
49
+ this.pendingStatePartCreation.set(statePartNameArg, creationPromise);
50
+ try {
51
+ const result = await creationPromise;
52
+ return result;
53
+ }
54
+ finally {
55
+ this.pendingStatePartCreation.delete(statePartNameArg);
42
56
  }
43
57
  }
44
58
  /**
@@ -56,12 +70,19 @@ export class Smartstate {
56
70
  : null);
57
71
  await newState.init();
58
72
  const currentState = newState.getState();
59
- await newState.setState({
60
- ...currentState,
61
- ...initialPayloadArg,
62
- });
73
+ if (initMode === 'persistent' && currentState !== undefined) {
74
+ // Persisted state exists - merge with defaults, persisted values take precedence
75
+ await newState.setState({
76
+ ...initialPayloadArg,
77
+ ...currentState,
78
+ });
79
+ }
80
+ else {
81
+ // No persisted state or non-persistent mode
82
+ await newState.setState(initialPayloadArg);
83
+ }
63
84
  this.statePartMap[statePartName] = newState;
64
85
  return newState;
65
86
  }
66
87
  }
67
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRzdGF0ZS5jbGFzc2VzLnNtYXJ0c3RhdGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydHN0YXRlLmNsYXNzZXMuc21hcnRzdGF0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHlCQUF5QixDQUFDO0FBQ25ELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUk5RDs7R0FFRztBQUNILE1BQU0sT0FBTyxVQUFVO0lBR3JCO1FBRk8saUJBQVksR0FBdUUsRUFBRSxDQUFDO0lBRTlFLENBQUM7SUFFaEI7Ozs7Ozs7OztPQVNHO0lBQ0ksS0FBSyxDQUFDLFlBQVksQ0FDdkIsZ0JBQW1DLEVBQ25DLFVBQXdCLEVBQ3hCLFdBQXNCLE1BQU07UUFFNUIsTUFBTSxpQkFBaUIsR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDLGdCQUFnQixDQUFDLENBQUM7UUFFOUQsSUFBSSxpQkFBaUIsRUFBRSxDQUFDO1lBQ3RCLFFBQVEsUUFBUSxFQUFFLENBQUM7Z0JBQ2pCLEtBQUssV0FBVztvQkFDZCxNQUFNLElBQUksS0FBSyxDQUNiLGVBQWUsZ0JBQWdCLCtDQUErQyxDQUMvRSxDQUFDO2dCQUNKLEtBQUssT0FBTztvQkFDVixvQ0FBb0M7b0JBQ3BDLE9BQU8sSUFBSSxDQUFDLGVBQWUsQ0FBYyxnQkFBZ0IsRUFBRSxVQUFVLEVBQUUsUUFBUSxDQUFDLENBQUM7Z0JBQ25GLEtBQUssTUFBTSxDQUFDO2dCQUNaLEtBQUssWUFBWSxDQUFDO2dCQUNsQjtvQkFDRSw2QkFBNkI7b0JBQzdCLE9BQU8saUJBQThELENBQUM7WUFDMUUsQ0FBQztRQUNILENBQUM7YUFBTSxDQUFDO1lBQ04sMkJBQTJCO1lBQzNCLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztnQkFDaEIsTUFBTSxJQUFJLEtBQUssQ0FDYixlQUFlLGdCQUFnQixnREFBZ0QsQ0FDaEYsQ0FBQztZQUNKLENBQUM7WUFDRCxPQUFPLElBQUksQ0FBQyxlQUFlLENBQWMsZ0JBQWdCLEVBQUUsVUFBVSxFQUFFLFFBQVEsQ0FBQyxDQUFDO1FBQ25GLENBQUM7SUFDSCxDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSyxLQUFLLENBQUMsZUFBZSxDQUMzQixhQUFnQyxFQUNoQyxpQkFBOEIsRUFDOUIsV0FBc0IsTUFBTTtRQUU1QixNQUFNLFFBQVEsR0FBRyxJQUFJLFNBQVMsQ0FDNUIsYUFBYSxFQUNiLFFBQVEsS0FBSyxZQUFZO1lBQ3ZCLENBQUMsQ0FBQztnQkFDRSxNQUFNLEVBQUUsWUFBWTtnQkFDcEIsU0FBUyxFQUFFLGFBQWE7YUFDekI7WUFDSCxDQUFDLENBQUMsSUFBSSxDQUNULENBQUM7UUFDRixNQUFNLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUN0QixNQUFNLFlBQVksR0FBRyxRQUFRLENBQUMsUUFBUSxFQUFFLENBQUM7UUFDekMsTUFBTSxRQUFRLENBQUMsUUFBUSxDQUFDO1lBQ3RCLEdBQUcsWUFBWTtZQUNmLEdBQUcsaUJBQWlCO1NBQ3JCLENBQUMsQ0FBQztRQUNILElBQUksQ0FBQyxZQUFZLENBQUMsYUFBYSxDQUFDLEdBQUcsUUFBUSxDQUFDO1FBQzVDLE9BQU8sUUFBUSxDQUFDO0lBQ2xCLENBQUM7Q0FDRiJ9
88
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRzdGF0ZS5jbGFzc2VzLnNtYXJ0c3RhdGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydHN0YXRlLmNsYXNzZXMuc21hcnRzdGF0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHlCQUF5QixDQUFDO0FBQ25ELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUk5RDs7R0FFRztBQUNILE1BQU0sT0FBTyxVQUFVO0lBS3JCO1FBSk8saUJBQVksR0FBdUUsRUFBRSxDQUFDO1FBRXJGLDZCQUF3QixHQUE0RCxJQUFJLEdBQUcsRUFBRSxDQUFDO0lBRXZGLENBQUM7SUFFaEI7Ozs7Ozs7OztPQVNHO0lBQ0ksS0FBSyxDQUFDLFlBQVksQ0FDdkIsZ0JBQW1DLEVBQ25DLFVBQXdCLEVBQ3hCLFdBQXNCLE1BQU07UUFFNUIseUVBQXlFO1FBQ3pFLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyx3QkFBd0IsQ0FBQyxHQUFHLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztRQUNwRSxJQUFJLE9BQU8sRUFBRSxDQUFDO1lBQ1osT0FBTyxPQUE2RCxDQUFDO1FBQ3ZFLENBQUM7UUFFRCxNQUFNLGlCQUFpQixHQUFHLElBQUksQ0FBQyxZQUFZLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztRQUU5RCxJQUFJLGlCQUFpQixFQUFFLENBQUM7WUFDdEIsUUFBUSxRQUFRLEVBQUUsQ0FBQztnQkFDakIsS0FBSyxXQUFXO29CQUNkLE1BQU0sSUFBSSxLQUFLLENBQ2IsZUFBZSxnQkFBZ0IsK0NBQStDLENBQy9FLENBQUM7Z0JBQ0osS0FBSyxPQUFPO29CQUNWLG9DQUFvQztvQkFDcEMsTUFBTSxDQUFDLDJCQUEyQjtnQkFDcEMsS0FBSyxNQUFNLENBQUM7Z0JBQ1osS0FBSyxZQUFZLENBQUM7Z0JBQ2xCO29CQUNFLDZCQUE2QjtvQkFDN0IsT0FBTyxpQkFBOEQsQ0FBQztZQUMxRSxDQUFDO1FBQ0gsQ0FBQzthQUFNLENBQUM7WUFDTiwyQkFBMkI7WUFDM0IsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO2dCQUNoQixNQUFNLElBQUksS0FBSyxDQUNiLGVBQWUsZ0JBQWdCLGdEQUFnRCxDQUNoRixDQUFDO1lBQ0osQ0FBQztRQUNILENBQUM7UUFFRCxNQUFNLGVBQWUsR0FBRyxJQUFJLENBQUMsZUFBZSxDQUFjLGdCQUFnQixFQUFFLFVBQVUsRUFBRSxRQUFRLENBQUMsQ0FBQztRQUNsRyxJQUFJLENBQUMsd0JBQXdCLENBQUMsR0FBRyxDQUFDLGdCQUFnQixFQUFFLGVBQWUsQ0FBQyxDQUFDO1FBRXJFLElBQUksQ0FBQztZQUNILE1BQU0sTUFBTSxHQUFHLE1BQU0sZUFBZSxDQUFDO1lBQ3JDLE9BQU8sTUFBTSxDQUFDO1FBQ2hCLENBQUM7Z0JBQVMsQ0FBQztZQUNULElBQUksQ0FBQyx3QkFBd0IsQ0FBQyxNQUFNLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztRQUN6RCxDQUFDO0lBQ0gsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0ssS0FBSyxDQUFDLGVBQWUsQ0FDM0IsYUFBZ0MsRUFDaEMsaUJBQThCLEVBQzlCLFdBQXNCLE1BQU07UUFFNUIsTUFBTSxRQUFRLEdBQUcsSUFBSSxTQUFTLENBQzVCLGFBQWEsRUFDYixRQUFRLEtBQUssWUFBWTtZQUN2QixDQUFDLENBQUM7Z0JBQ0UsTUFBTSxFQUFFLFlBQVk7Z0JBQ3BCLFNBQVMsRUFBRSxhQUFhO2FBQ3pCO1lBQ0gsQ0FBQyxDQUFDLElBQUksQ0FDVCxDQUFDO1FBQ0YsTUFBTSxRQUFRLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDdEIsTUFBTSxZQUFZLEdBQUcsUUFBUSxDQUFDLFFBQVEsRUFBRSxDQUFDO1FBRXpDLElBQUksUUFBUSxLQUFLLFlBQVksSUFBSSxZQUFZLEtBQUssU0FBUyxFQUFFLENBQUM7WUFDNUQsaUZBQWlGO1lBQ2pGLE1BQU0sUUFBUSxDQUFDLFFBQVEsQ0FBQztnQkFDdEIsR0FBRyxpQkFBaUI7Z0JBQ3BCLEdBQUcsWUFBWTthQUNoQixDQUFDLENBQUM7UUFDTCxDQUFDO2FBQU0sQ0FBQztZQUNOLDRDQUE0QztZQUM1QyxNQUFNLFFBQVEsQ0FBQyxRQUFRLENBQUMsaUJBQWlCLENBQUMsQ0FBQztRQUM3QyxDQUFDO1FBRUQsSUFBSSxDQUFDLFlBQVksQ0FBQyxhQUFhLENBQUMsR0FBRyxRQUFRLENBQUM7UUFDNUMsT0FBTyxRQUFRLENBQUM7SUFDbEIsQ0FBQztDQUNGIn0=
@@ -5,6 +5,7 @@ export declare class StatePart<TStatePartName, TStatePayload> {
5
5
  state: plugins.smartrx.rxjs.Subject<TStatePayload>;
6
6
  stateStore: TStatePayload | undefined;
7
7
  private cumulativeDeferred;
8
+ private pendingCumulativeNotification;
8
9
  private webStoreOptions;
9
10
  private webStore;
10
11
  constructor(nameArg: TStatePartName, webStoreOptionsArg?: plugins.webstore.IWebStoreOptions);
@@ -50,8 +51,9 @@ export declare class StatePart<TStatePartName, TStatePayload> {
50
51
  /**
51
52
  * waits until a certain part of the state becomes available
52
53
  * @param selectorFn
54
+ * @param timeoutMs - optional timeout in milliseconds to prevent indefinite waiting
53
55
  */
54
- waitUntilPresent<T = TStatePayload>(selectorFn?: (state: TStatePayload) => T): Promise<T>;
56
+ waitUntilPresent<T = TStatePayload>(selectorFn?: (state: TStatePayload) => T, timeoutMs?: number): Promise<T>;
55
57
  /**
56
58
  * is executed
57
59
  */
@@ -4,6 +4,7 @@ export class StatePart {
4
4
  constructor(nameArg, webStoreOptionsArg) {
5
5
  this.state = new plugins.smartrx.rxjs.Subject();
6
6
  this.cumulativeDeferred = plugins.smartpromise.cumulativeDefer();
7
+ this.pendingCumulativeNotification = null;
7
8
  this.webStore = null; // Add WebStore instance
8
9
  this.name = nameArg;
9
10
  // Initialize WebStore if webStoreOptions are provided
@@ -40,12 +41,13 @@ export class StatePart {
40
41
  if (!this.validateState(newStateArg)) {
41
42
  throw new Error(`Invalid state structure for state part '${this.name}'`);
42
43
  }
43
- this.stateStore = newStateArg;
44
- await this.notifyChange();
45
- // Save state to WebStore if initialized
44
+ // Save to WebStore first to ensure atomicity - if save fails, memory state remains unchanged
46
45
  if (this.webStore) {
47
46
  await this.webStore.set(String(this.name), newStateArg);
48
47
  }
48
+ // Update in-memory state after successful persistence
49
+ this.stateStore = newStateArg;
50
+ await this.notifyChange();
49
51
  return this.stateStore;
50
52
  }
51
53
  /**
@@ -81,8 +83,12 @@ export class StatePart {
81
83
  * creates a cumulative notification by adding a change notification at the end of the call stack;
82
84
  */
83
85
  notifyChangeCumulative() {
84
- // TODO: check viability
85
- setTimeout(async () => {
86
+ // Debounce: clear any pending notification
87
+ if (this.pendingCumulativeNotification) {
88
+ clearTimeout(this.pendingCumulativeNotification);
89
+ }
90
+ this.pendingCumulativeNotification = setTimeout(async () => {
91
+ this.pendingCumulativeNotification = null;
86
92
  if (this.stateStore) {
87
93
  await this.notifyChange();
88
94
  }
@@ -100,7 +106,8 @@ export class StatePart {
100
106
  return selectorFn(stateArg);
101
107
  }
102
108
  catch (e) {
103
- // Nothing here
109
+ console.error(`Selector error in state part '${this.name}':`, e);
110
+ return undefined;
104
111
  }
105
112
  }));
106
113
  return mapped;
@@ -123,18 +130,37 @@ export class StatePart {
123
130
  /**
124
131
  * waits until a certain part of the state becomes available
125
132
  * @param selectorFn
133
+ * @param timeoutMs - optional timeout in milliseconds to prevent indefinite waiting
126
134
  */
127
- async waitUntilPresent(selectorFn) {
135
+ async waitUntilPresent(selectorFn, timeoutMs) {
128
136
  const done = plugins.smartpromise.defer();
129
137
  const selectedObservable = this.select(selectorFn);
130
- const subscription = selectedObservable.subscribe(async (value) => {
131
- if (value) {
138
+ let resolved = false;
139
+ const subscription = selectedObservable.subscribe((value) => {
140
+ if (value && !resolved) {
141
+ resolved = true;
132
142
  done.resolve(value);
133
143
  }
134
144
  });
135
- const result = await done.promise;
136
- subscription.unsubscribe();
137
- return result;
145
+ let timeoutId;
146
+ if (timeoutMs) {
147
+ timeoutId = setTimeout(() => {
148
+ if (!resolved) {
149
+ resolved = true;
150
+ subscription.unsubscribe();
151
+ done.reject(new Error(`waitUntilPresent timed out after ${timeoutMs}ms`));
152
+ }
153
+ }, timeoutMs);
154
+ }
155
+ try {
156
+ const result = await done.promise;
157
+ return result;
158
+ }
159
+ finally {
160
+ subscription.unsubscribe();
161
+ if (timeoutId)
162
+ clearTimeout(timeoutId);
163
+ }
138
164
  }
139
165
  /**
140
166
  * is executed
@@ -142,7 +168,7 @@ export class StatePart {
142
168
  async stateSetup(funcArg) {
143
169
  const resultPromise = funcArg(this);
144
170
  this.cumulativeDeferred.addPromise(resultPromise);
145
- this.setState(await resultPromise);
171
+ await this.setState(await resultPromise);
146
172
  }
147
173
  }
148
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRzdGF0ZS5jbGFzc2VzLnN0YXRlcGFydC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYXJ0c3RhdGUuY2xhc3Nlcy5zdGF0ZXBhcnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSx5QkFBeUIsQ0FBQztBQUNuRCxPQUFPLEVBQUUsV0FBVyxFQUFtQixNQUFNLHFDQUFxQyxDQUFDO0FBRW5GLE1BQU0sT0FBTyxTQUFTO0lBU3BCLFlBQVksT0FBdUIsRUFBRSxrQkFBc0Q7UUFQcEYsVUFBSyxHQUFHLElBQUksT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFpQixDQUFDO1FBRXpELHVCQUFrQixHQUFHLE9BQU8sQ0FBQyxZQUFZLENBQUMsZUFBZSxFQUFFLENBQUM7UUFHNUQsYUFBUSxHQUFvRCxJQUFJLENBQUMsQ0FBQyx3QkFBd0I7UUFHaEcsSUFBSSxDQUFDLElBQUksR0FBRyxPQUFPLENBQUM7UUFFcEIsc0RBQXNEO1FBQ3RELElBQUksa0JBQWtCLEVBQUUsQ0FBQztZQUN2QixJQUFJLENBQUMsZUFBZSxHQUFHLGtCQUFrQixDQUFDO1FBQzVDLENBQUM7SUFDSCxDQUFDO0lBRUQ7O09BRUc7SUFDSSxLQUFLLENBQUMsSUFBSTtRQUNmLElBQUksSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDO1lBQ3pCLElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxPQUFPLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBZ0IsSUFBSSxDQUFDLGVBQWUsQ0FBQyxDQUFDO1lBQ25GLE1BQU0sSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUMzQixNQUFNLFdBQVcsR0FBRyxNQUFNLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztZQUMvRCxJQUFJLFdBQVcsSUFBSSxJQUFJLENBQUMsYUFBYSxDQUFDLFdBQVcsQ0FBQyxFQUFFLENBQUM7Z0JBQ25ELElBQUksQ0FBQyxVQUFVLEdBQUcsV0FBVyxDQUFDO2dCQUM5QixNQUFNLElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQztZQUM1QixDQUFDO1FBQ0gsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNJLFFBQVE7UUFDYixPQUFPLElBQUksQ0FBQyxVQUFVLENBQUM7SUFDekIsQ0FBQztJQUVEOzs7T0FHRztJQUNJLEtBQUssQ0FBQyxRQUFRLENBQUMsV0FBMEI7UUFDOUMsMkJBQTJCO1FBQzNCLElBQUksQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLFdBQVcsQ0FBQyxFQUFFLENBQUM7WUFDckMsTUFBTSxJQUFJLEtBQUssQ0FBQywyQ0FBMkMsSUFBSSxDQUFDLElBQUksR0FBRyxDQUFDLENBQUM7UUFDM0UsQ0FBQztRQUVELElBQUksQ0FBQyxVQUFVLEdBQUcsV0FBVyxDQUFDO1FBQzlCLE1BQU0sSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO1FBRTFCLHdDQUF3QztRQUN4QyxJQUFJLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztZQUNsQixNQUFNLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsV0FBVyxDQUFDLENBQUM7UUFDMUQsQ0FBQztRQUNELE9BQU8sSUFBSSxDQUFDLFVBQVUsQ0FBQztJQUN6QixDQUFDO0lBRUQ7OztPQUdHO0lBQ08sYUFBYSxDQUFDLFFBQWE7UUFDbkMsd0RBQXdEO1FBQ3hELHVEQUF1RDtRQUN2RCxPQUFPLFFBQVEsS0FBSyxJQUFJLElBQUksUUFBUSxLQUFLLFNBQVMsQ0FBQztJQUNyRCxDQUFDO0lBRUQ7O09BRUc7SUFDSSxLQUFLLENBQUMsWUFBWTtRQUN2QixJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO1lBQ3JCLE9BQU87UUFDVCxDQUFDO1FBQ0QsTUFBTSxlQUFlLEdBQUcsS0FBSyxFQUFFLFFBQWEsRUFBRSxFQUFFO1lBQzlDLE9BQU8sTUFBTSxPQUFPLENBQUMsWUFBWSxDQUFDLGdCQUFnQixDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMscUJBQXFCLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQztRQUN4RyxDQUFDLENBQUM7UUFDRixNQUFNLFdBQVcsR0FBRyxNQUFNLGVBQWUsQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDM0QsSUFDRSxJQUFJLENBQUMsZ0NBQWdDO1lBQ3JDLFdBQVcsS0FBSyxJQUFJLENBQUMsZ0NBQWdDLEVBQ3JELENBQUM7WUFDRCxPQUFPO1FBQ1QsQ0FBQzthQUFNLENBQUM7WUFDTixJQUFJLENBQUMsZ0NBQWdDLEdBQUcsV0FBVyxDQUFDO1FBQ3RELENBQUM7UUFDRCxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLENBQUM7SUFDbkMsQ0FBQztJQUdEOztPQUVHO0lBQ0ksc0JBQXNCO1FBQzNCLHdCQUF3QjtRQUN4QixVQUFVLENBQUMsS0FBSyxJQUFJLEVBQUU7WUFDcEIsSUFBSSxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7Z0JBQ3BCLE1BQU0sSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO1lBQzVCLENBQUM7UUFDSCxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDUixDQUFDO0lBRUQ7O09BRUc7SUFDSSxNQUFNLENBQ1gsVUFBd0M7UUFFeEMsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO1lBQ2hCLFVBQVUsR0FBRyxDQUFDLEtBQW9CLEVBQUUsRUFBRSxDQUFVLEtBQU0sQ0FBQztRQUN6RCxDQUFDO1FBQ0QsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQzVCLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDLEVBQ25ELE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQyxRQUFRLEVBQTZCLEVBQUUsQ0FBQyxRQUFRLEtBQUssU0FBUyxDQUFDLEVBQ2hHLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQyxRQUFRLEVBQUUsRUFBRTtZQUN4QyxJQUFJLENBQUM7Z0JBQ0gsT0FBTyxVQUFVLENBQUMsUUFBUSxDQUFDLENBQUM7WUFDOUIsQ0FBQztZQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7Z0JBQ1gsZUFBZTtZQUNqQixDQUFDO1FBQ0gsQ0FBQyxDQUFDLENBQ0gsQ0FBQztRQUNGLE9BQU8sTUFBTSxDQUFDO0lBQ2hCLENBQUM7SUFFRDs7T0FFRztJQUNJLFlBQVksQ0FDakIsU0FBb0Q7UUFFcEQsT0FBTyxJQUFJLFdBQVcsQ0FBQyxJQUFJLEVBQUUsU0FBUyxDQUFDLENBQUM7SUFDMUMsQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLGNBQWMsQ0FBSSxXQUEwQyxFQUFFLGFBQWdCO1FBQ3pGLE1BQU0sSUFBSSxDQUFDLGtCQUFrQixDQUFDLE9BQU8sQ0FBQztRQUN0QyxNQUFNLFFBQVEsR0FBRyxNQUFNLFdBQVcsQ0FBQyxTQUFTLENBQUMsSUFBSSxFQUFFLGFBQWEsQ0FBQyxDQUFDO1FBQ2xFLE1BQU0sSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUM5QixPQUFPLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztJQUN6QixDQUFDO0lBRUQ7OztPQUdHO0lBQ0ksS0FBSyxDQUFDLGdCQUFnQixDQUMzQixVQUF3QztRQUV4QyxNQUFNLElBQUksR0FBRyxPQUFPLENBQUMsWUFBWSxDQUFDLEtBQUssRUFBSyxDQUFDO1FBQzdDLE1BQU0sa0JBQWtCLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUNuRCxNQUFNLFlBQVksR0FBRyxrQkFBa0IsQ0FBQyxTQUFTLENBQUMsS0FBSyxFQUFFLEtBQUssRUFBRSxFQUFFO1lBQ2hFLElBQUksS0FBSyxFQUFFLENBQUM7Z0JBQ1YsSUFBSSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUN0QixDQUFDO1FBQ0gsQ0FBQyxDQUFDLENBQUM7UUFDSCxNQUFNLE1BQU0sR0FBRyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUM7UUFDbEMsWUFBWSxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQzNCLE9BQU8sTUFBTSxDQUFDO0lBQ2hCLENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxVQUFVLENBQ3JCLE9BQWlGO1FBRWpGLE1BQU0sYUFBYSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNwQyxJQUFJLENBQUMsa0JBQWtCLENBQUMsVUFBVSxDQUFDLGFBQWEsQ0FBQyxDQUFDO1FBQ2xELElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTSxhQUFhLENBQUMsQ0FBQztJQUNyQyxDQUFDO0NBQ0YifQ==
174
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRzdGF0ZS5jbGFzc2VzLnN0YXRlcGFydC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYXJ0c3RhdGUuY2xhc3Nlcy5zdGF0ZXBhcnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSx5QkFBeUIsQ0FBQztBQUNuRCxPQUFPLEVBQUUsV0FBVyxFQUFtQixNQUFNLHFDQUFxQyxDQUFDO0FBRW5GLE1BQU0sT0FBTyxTQUFTO0lBV3BCLFlBQVksT0FBdUIsRUFBRSxrQkFBc0Q7UUFUcEYsVUFBSyxHQUFHLElBQUksT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFpQixDQUFDO1FBRXpELHVCQUFrQixHQUFHLE9BQU8sQ0FBQyxZQUFZLENBQUMsZUFBZSxFQUFFLENBQUM7UUFFNUQsa0NBQTZCLEdBQXlDLElBQUksQ0FBQztRQUczRSxhQUFRLEdBQW9ELElBQUksQ0FBQyxDQUFDLHdCQUF3QjtRQUdoRyxJQUFJLENBQUMsSUFBSSxHQUFHLE9BQU8sQ0FBQztRQUVwQixzREFBc0Q7UUFDdEQsSUFBSSxrQkFBa0IsRUFBRSxDQUFDO1lBQ3ZCLElBQUksQ0FBQyxlQUFlLEdBQUcsa0JBQWtCLENBQUM7UUFDNUMsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxJQUFJO1FBQ2YsSUFBSSxJQUFJLENBQUMsZUFBZSxFQUFFLENBQUM7WUFDekIsSUFBSSxDQUFDLFFBQVEsR0FBRyxJQUFJLE9BQU8sQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFnQixJQUFJLENBQUMsZUFBZSxDQUFDLENBQUM7WUFDbkYsTUFBTSxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSxDQUFDO1lBQzNCLE1BQU0sV0FBVyxHQUFHLE1BQU0sSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1lBQy9E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
package/npmextra.json CHANGED
@@ -1,9 +1,16 @@
1
1
  {
2
- "npmci": {
3
- "npmGlobalTools": [],
4
- "npmAccessLevel": "public"
2
+ "@git.zone/tsbundle": {
3
+ "bundles": [
4
+ {
5
+ "from": "./ts/index.ts",
6
+ "to": "./dist_bundle/bundle.js",
7
+ "outputMode": "bundle",
8
+ "bundler": "esbuild",
9
+ "production": true
10
+ }
11
+ ]
5
12
  },
6
- "gitzone": {
13
+ "@git.zone/cli": {
7
14
  "projectType": "npm",
8
15
  "module": {
9
16
  "githost": "code.foss.global",
@@ -24,9 +31,19 @@
24
31
  "asynchronous state",
25
32
  "cumulative notification"
26
33
  ]
34
+ },
35
+ "release": {
36
+ "registries": [
37
+ "https://verdaccio.lossless.digital",
38
+ "https://registry.npmjs.org"
39
+ ],
40
+ "accessLevel": "public"
27
41
  }
28
42
  },
29
- "tsdoc": {
43
+ "@git.zone/tsdoc": {
30
44
  "legal": "\n## License and Legal Information\n\nThis repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository. \n\n**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.\n\n### Trademarks\n\nThis project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.\n\n### Company Information\n\nTask Venture Capital GmbH \nRegistered at District court Bremen HRB 35230 HB, Germany\n\nFor any legal inquiries or if you require further information, please contact us via email at hello@task.vc.\n\nBy using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.\n"
45
+ },
46
+ "@ship.zone/szci": {
47
+ "npmGlobalTools": []
31
48
  }
32
49
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@push.rocks/smartstate",
3
- "version": "2.0.27",
3
+ "version": "2.0.31",
4
4
  "private": false,
5
5
  "description": "A package for handling and managing state in applications.",
6
6
  "main": "dist_ts/index.js",
@@ -8,18 +8,23 @@
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
- "@git.zone/tsbuild": "^2.6.8",
13
- "@git.zone/tsbundle": "^2.5.1",
14
- "@git.zone/tsrun": "^1.3.3",
15
- "@git.zone/tstest": "^2.3.8",
17
+ "@git.zone/tsbuild": "^4.1.2",
18
+ "@git.zone/tsbundle": "^2.9.0",
19
+ "@git.zone/tsrun": "^2.0.1",
20
+ "@git.zone/tstest": "^3.1.8",
16
21
  "@push.rocks/tapbundle": "^6.0.3",
17
- "@types/node": "^22.7.4"
22
+ "@types/node": "^25.3.2"
18
23
  },
19
24
  "dependencies": {
20
25
  "@push.rocks/lik": "^6.2.2",
21
26
  "@push.rocks/smarthash": "^3.2.6",
22
- "@push.rocks/smartjson": "^5.2.0",
27
+ "@push.rocks/smartjson": "^6.0.0",
23
28
  "@push.rocks/smartpromise": "^4.2.3",
24
29
  "@push.rocks/smartrx": "^3.0.10",
25
30
  "@push.rocks/webstore": "^2.0.20"
@@ -56,9 +61,5 @@
56
61
  "type": "git",
57
62
  "url": "https://code.foss.global/push.rocks/smartstate.git"
58
63
  },
59
- "scripts": {
60
- "test": "(tstest test/ --verbose)",
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,6 +1,6 @@
1
1
  # Smartstate Implementation Notes
2
2
 
3
- ## Current API (as of v2.0.24+)
3
+ ## Current API (as of v2.0.28+)
4
4
 
5
5
  ### State Part Initialization
6
6
  - State parts can be created with different init modes: 'soft' (default), 'mandatory', 'force', 'persistent'
@@ -49,4 +49,12 @@
49
49
  4. Simplified init mode logic with clear behavior for each mode
50
50
  5. Added state validation with extensible validateState() method
51
51
  6. Made notifyChange() async to support proper hash comparison
52
- 7. Updated select() to filter undefined states
52
+ 7. Updated select() to filter undefined states
53
+
54
+ ## Dependency Versions (v2.0.30)
55
+ - @git.zone/tsbuild: ^4.1.2
56
+ - @git.zone/tsbundle: ^2.9.0
57
+ - @git.zone/tsrun: ^2.0.1
58
+ - @git.zone/tstest: ^3.1.8
59
+ - @push.rocks/smartjson: ^6.0.0
60
+ - @types/node: ^25.3.2
package/readme.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # @push.rocks/smartstate
2
- A powerful TypeScript library for elegant state management using RxJS and reactive programming patterns
2
+
3
+ A powerful TypeScript library for elegant state management using RxJS and reactive programming patterns 🚀
4
+
5
+ ## Issue Reporting and Security
6
+
7
+ For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly.
3
8
 
4
9
  ## Install
5
10
 
@@ -16,15 +21,13 @@ npm install @push.rocks/smartstate --save
16
21
  yarn add @push.rocks/smartstate
17
22
  ```
18
23
 
19
- This will add `@push.rocks/smartstate` to your project's dependencies.
20
-
21
24
  ## Usage
22
25
 
23
- The `@push.rocks/smartstate` library provides an elegant way to handle state within your JavaScript or TypeScript projects, leveraging the power of Reactive Extensions (RxJS) and a structured state management strategy. In the following sections, we will explore the comprehensive capabilities of this package and how to effectively use them in various scenarios, ensuring a robust state management pattern in your applications.
26
+ The `@push.rocks/smartstate` library provides an elegant way to handle state within your JavaScript or TypeScript projects, leveraging the power of Reactive Extensions (RxJS) and a structured state management strategy.
24
27
 
25
28
  ### Getting Started
26
29
 
27
- First, let's import the necessary components from the library:
30
+ Import the necessary components from the library:
28
31
 
29
32
  ```typescript
30
33
  import { Smartstate, StatePart, StateAction } from '@push.rocks/smartstate';
@@ -32,7 +35,7 @@ import { Smartstate, StatePart, StateAction } from '@push.rocks/smartstate';
32
35
 
33
36
  ### Creating a SmartState Instance
34
37
 
35
- `Smartstate` acts as the container for your state parts. You can consider it as the root of your state management structure.
38
+ `Smartstate` acts as the container for your state parts. Think of it as the root of your state management structure:
36
39
 
37
40
  ```typescript
38
41
  const myAppSmartState = new Smartstate<YourStatePartNamesEnum>();
@@ -42,16 +45,16 @@ const myAppSmartState = new Smartstate<YourStatePartNamesEnum>();
42
45
 
43
46
  When creating state parts, you can specify different initialization modes:
44
47
 
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
48
+ | Mode | Description |
49
+ |------|-------------|
50
+ | `'soft'` | Default. Returns existing state part if it exists, creates new if not |
51
+ | `'mandatory'` | Requires state part to not exist, throws error if it does |
52
+ | `'force'` | Always creates new state part, overwriting any existing one |
53
+ | `'persistent'` | Like 'soft' but with WebStore persistence using IndexedDB |
49
54
 
50
55
  ### Defining State Parts
51
56
 
52
- State parts represent separable sections of your state, making it easier to manage and modularize. For example, you may have a state part for user data and another for application settings.
53
-
54
- Define state part names using either enums or string literal types:
57
+ State parts represent separable sections of your state, making it easier to manage and modularize. Define state part names using either enums or string literal types:
55
58
 
56
59
  ```typescript
57
60
  // Option 1: Using enums
@@ -64,7 +67,7 @@ enum AppStateParts {
64
67
  type AppStateParts = 'UserState' | 'SettingsState';
65
68
  ```
66
69
 
67
- Now, let's create a state part within our `myAppSmartState` instance:
70
+ Create a state part within your `Smartstate` instance:
68
71
 
69
72
  ```typescript
70
73
  interface IUserState {
@@ -77,13 +80,11 @@ const userStatePart = await myAppSmartState.getStatePart<IUserState>(
77
80
  { isLoggedIn: false }, // Initial state
78
81
  'soft' // Init mode (optional, defaults to 'soft')
79
82
  );
80
-
81
- // Note: Persistent state parts are automatically initialized internally
82
83
  ```
83
84
 
84
85
  ### Subscribing to State Changes
85
86
 
86
- You can subscribe to changes in a state part to perform actions accordingly:
87
+ Subscribe to changes in a state part to perform actions accordingly:
87
88
 
88
89
  ```typescript
89
90
  // The select() method automatically filters out undefined states
@@ -92,7 +93,7 @@ userStatePart.select().subscribe((currentState) => {
92
93
  });
93
94
  ```
94
95
 
95
- If you need to select a specific part of your state, you can pass a selector function:
96
+ Select a specific part of your state with a selector function:
96
97
 
97
98
  ```typescript
98
99
  userStatePart.select(state => state.username).subscribe((username) => {
@@ -116,8 +117,6 @@ const loginUserAction = userStatePart.createAction<ILoginPayload>(async (statePa
116
117
  });
117
118
 
118
119
  // Dispatch the action to update the state
119
- loginUserAction.trigger({ username: 'johnDoe' });
120
- // or await the result
121
120
  const newState = await loginUserAction.trigger({ username: 'johnDoe' });
122
121
  ```
123
122
 
@@ -128,14 +127,12 @@ There are two ways to dispatch actions:
128
127
  ```typescript
129
128
  // Method 1: Using trigger on the action (returns promise)
130
129
  const newState = await loginUserAction.trigger({ username: 'johnDoe' });
131
- // or fire and forget
132
- loginUserAction.trigger({ username: 'johnDoe' });
133
130
 
134
131
  // Method 2: Using dispatchAction on the state part (returns promise)
135
132
  const newState = await userStatePart.dispatchAction(loginUserAction, { username: 'johnDoe' });
136
133
  ```
137
134
 
138
- Both methods return a Promise with the new state, giving you flexibility in how you handle the result.
135
+ Both methods return a Promise with the new state payload.
139
136
 
140
137
  ### Additional State Methods
141
138
 
@@ -148,20 +145,26 @@ if (currentState) {
148
145
  console.log('Current user:', currentState.username);
149
146
  }
150
147
 
151
- // Wait for a specific state condition
148
+ // Wait for state to be present
152
149
  await userStatePart.waitUntilPresent();
153
150
 
154
151
  // Wait for a specific property to be present
155
152
  await userStatePart.waitUntilPresent(state => state.username);
156
153
 
154
+ // Wait with a timeout (throws error if condition not met within timeout)
155
+ try {
156
+ await userStatePart.waitUntilPresent(state => state.username, 5000); // 5 second timeout
157
+ } catch (error) {
158
+ console.error('Timed out waiting for username');
159
+ }
160
+
157
161
  // Setup initial state with async operations
158
162
  await userStatePart.stateSetup(async (statePart) => {
159
- // Perform async initialization
160
163
  const userData = await fetchUserData();
161
164
  return { ...statePart.getState(), ...userData };
162
165
  });
163
166
 
164
- // Defer notification to end of call stack
167
+ // Defer notification to end of call stack (debounced)
165
168
  userStatePart.notifyChangeCumulative();
166
169
  ```
167
170
 
@@ -172,31 +175,28 @@ userStatePart.notifyChangeCumulative();
172
175
  ```typescript
173
176
  const settingsStatePart = await myAppSmartState.getStatePart<ISettingsState>(
174
177
  AppStateParts.SettingsState,
175
- { theme: 'light' }, // Initial state
178
+ { theme: 'light' }, // Initial/default state
176
179
  'persistent' // Mode
177
180
  );
178
-
179
- // Note: init() is called automatically for persistent mode
180
181
  ```
181
182
 
182
183
  Persistent state automatically:
183
184
  - Saves state changes to IndexedDB
184
185
  - Restores state on application restart
185
- - Manages storage with configurable database and store names
186
+ - Merges persisted values with defaults (persisted values take precedence)
187
+ - Ensures atomic writes (persistence happens before memory update)
186
188
 
187
189
  ### State Validation
188
190
 
189
191
  `Smartstate` includes built-in state validation to ensure data integrity:
190
192
 
191
193
  ```typescript
192
- // Basic validation (built-in)
193
- // Ensures state is not null or undefined
194
+ // Basic validation (built-in) ensures state is not null or undefined
194
195
  await userStatePart.setState(null); // Throws error: Invalid state structure
195
196
 
196
197
  // Custom validation by extending StatePart
197
198
  class ValidatedStatePart<T> extends StatePart<string, T> {
198
199
  protected validateState(stateArg: any): stateArg is T {
199
- // Add your custom validation logic
200
200
  return super.validateState(stateArg) && /* your validation */;
201
201
  }
202
202
  }
@@ -206,11 +206,12 @@ class ValidatedStatePart<T> extends StatePart<string, T> {
206
206
 
207
207
  `Smartstate` includes advanced performance optimizations:
208
208
 
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
211
- - **Cumulative Notifications**: Batch multiple state changes into a single notification using `notifyChangeCumulative()`
212
- - **Selective Subscriptions**: Use selectors to subscribe only to specific state properties
213
- - **Undefined State Filtering**: The `select()` method automatically filters out undefined states
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
211
+ - **📦 Cumulative Notifications**: Batch multiple state changes into a single notification using `notifyChangeCumulative()` with automatic debouncing
212
+ - **🎯 Selective Subscriptions**: Use selectors to subscribe only to specific state properties
213
+ - **✨ Undefined State Filtering**: The `select()` method automatically filters out undefined states
214
+ - **⚡ Concurrent Access Safety**: Prevents race conditions when multiple calls request the same state part simultaneously
214
215
 
215
216
  ### RxJS Integration
216
217
 
@@ -275,7 +276,7 @@ const loginAction = userState.createAction<{ username: string; email: string }>(
275
276
  async (statePart, payload) => {
276
277
  // Simulate API call
277
278
  await new Promise(resolve => setTimeout(resolve, 1000));
278
-
279
+
279
280
  return {
280
281
  isLoggedIn: true,
281
282
  username: payload.username,
@@ -293,34 +294,38 @@ userState.select(state => state.isLoggedIn).subscribe(isLoggedIn => {
293
294
  await loginAction.trigger({ username: 'john', email: 'john@example.com' });
294
295
  ```
295
296
 
296
- ### Key Features
297
-
298
- `@push.rocks/smartstate` provides a robust foundation for state management:
299
-
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
297
+ ## Key Features
298
+
299
+ | Feature | Description |
300
+ |---------|-------------|
301
+ | 🎯 **Type-safe** | Full TypeScript support with intelligent type inference |
302
+ | **Performance optimized** | Async state hash detection prevents unnecessary re-renders |
303
+ | 💾 **Persistent state** | Built-in IndexedDB support for state persistence |
304
+ | 🔄 **Reactive** | Powered by RxJS for elegant async handling |
305
+ | 🧩 **Modular** | Organize state into logical, reusable parts |
306
+ | **Validated** | Built-in state validation with extensible validation logic |
307
+ | 🎭 **Flexible init modes** | Choose how state parts are initialized |
308
+ | 📦 **Zero config** | Works out of the box with sensible defaults |
309
+ | 🛡️ **Race condition safe** | Concurrent state part creation is handled safely |
310
+ | ⏱️ **Timeout support** | `waitUntilPresent` supports optional timeouts |
308
311
 
309
312
  ## License and Legal Information
310
313
 
311
- This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
314
+ This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [license](./license) file.
312
315
 
313
316
  **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.
314
317
 
315
318
  ### Trademarks
316
319
 
317
- This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.
320
+ This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.
321
+
322
+ Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.
318
323
 
319
324
  ### Company Information
320
325
 
321
- Task Venture Capital GmbH
322
- Registered at District court Bremen HRB 35230 HB, Germany
326
+ Task Venture Capital GmbH
327
+ Registered at District Court Bremen HRB 35230 HB, Germany
323
328
 
324
- For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
329
+ For any legal inquiries or further information, please contact us via email at hello@task.vc.
325
330
 
326
331
  By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartstate',
6
- version: '2.0.27',
6
+ version: '2.0.31',
7
7
  description: 'A package for handling and managing state in applications.'
8
8
  }
@@ -9,6 +9,8 @@ export type TInitMode = 'soft' | 'mandatory' | 'force' | 'persistent';
9
9
  export class Smartstate<StatePartNameType extends string> {
10
10
  public statePartMap: { [key in StatePartNameType]?: StatePart<StatePartNameType, any> } = {};
11
11
 
12
+ private pendingStatePartCreation: Map<string, Promise<StatePart<StatePartNameType, any>>> = new Map();
13
+
12
14
  constructor() {}
13
15
 
14
16
  /**
@@ -26,8 +28,14 @@ export class Smartstate<StatePartNameType extends string> {
26
28
  initialArg?: PayloadType,
27
29
  initMode: TInitMode = 'soft'
28
30
  ): Promise<StatePart<StatePartNameType, PayloadType>> {
31
+ // Return pending creation if one exists to prevent duplicate state parts
32
+ const pending = this.pendingStatePartCreation.get(statePartNameArg);
33
+ if (pending) {
34
+ return pending as Promise<StatePart<StatePartNameType, PayloadType>>;
35
+ }
36
+
29
37
  const existingStatePart = this.statePartMap[statePartNameArg];
30
-
38
+
31
39
  if (existingStatePart) {
32
40
  switch (initMode) {
33
41
  case 'mandatory':
@@ -36,7 +44,7 @@ export class Smartstate<StatePartNameType extends string> {
36
44
  );
37
45
  case 'force':
38
46
  // Force mode: create new state part
39
- return this.createStatePart<PayloadType>(statePartNameArg, initialArg, initMode);
47
+ break; // Fall through to creation
40
48
  case 'soft':
41
49
  case 'persistent':
42
50
  default:
@@ -50,7 +58,16 @@ export class Smartstate<StatePartNameType extends string> {
50
58
  `State part '${statePartNameArg}' does not exist and no initial state provided`
51
59
  );
52
60
  }
53
- return this.createStatePart<PayloadType>(statePartNameArg, initialArg, initMode);
61
+ }
62
+
63
+ const creationPromise = this.createStatePart<PayloadType>(statePartNameArg, initialArg, initMode);
64
+ this.pendingStatePartCreation.set(statePartNameArg, creationPromise);
65
+
66
+ try {
67
+ const result = await creationPromise;
68
+ return result;
69
+ } finally {
70
+ this.pendingStatePartCreation.delete(statePartNameArg);
54
71
  }
55
72
  }
56
73
 
@@ -76,10 +93,18 @@ export class Smartstate<StatePartNameType extends string> {
76
93
  );
77
94
  await newState.init();
78
95
  const currentState = newState.getState();
79
- await newState.setState({
80
- ...currentState,
81
- ...initialPayloadArg,
82
- });
96
+
97
+ if (initMode === 'persistent' && currentState !== undefined) {
98
+ // Persisted state exists - merge with defaults, persisted values take precedence
99
+ await newState.setState({
100
+ ...initialPayloadArg,
101
+ ...currentState,
102
+ });
103
+ } else {
104
+ // No persisted state or non-persistent mode
105
+ await newState.setState(initialPayloadArg);
106
+ }
107
+
83
108
  this.statePartMap[statePartName] = newState;
84
109
  return newState;
85
110
  }
@@ -7,6 +7,8 @@ export class StatePart<TStatePartName, TStatePayload> {
7
7
  public stateStore: TStatePayload | undefined;
8
8
  private cumulativeDeferred = plugins.smartpromise.cumulativeDefer();
9
9
 
10
+ private pendingCumulativeNotification: ReturnType<typeof setTimeout> | null = null;
11
+
10
12
  private webStoreOptions: plugins.webstore.IWebStoreOptions;
11
13
  private webStore: plugins.webstore.WebStore<TStatePayload> | null = null; // Add WebStore instance
12
14
 
@@ -50,14 +52,16 @@ export class StatePart<TStatePartName, TStatePayload> {
50
52
  if (!this.validateState(newStateArg)) {
51
53
  throw new Error(`Invalid state structure for state part '${this.name}'`);
52
54
  }
53
-
54
- this.stateStore = newStateArg;
55
- await this.notifyChange();
56
-
57
- // Save state to WebStore if initialized
55
+
56
+ // Save to WebStore first to ensure atomicity - if save fails, memory state remains unchanged
58
57
  if (this.webStore) {
59
58
  await this.webStore.set(String(this.name), newStateArg);
60
59
  }
60
+
61
+ // Update in-memory state after successful persistence
62
+ this.stateStore = newStateArg;
63
+ await this.notifyChange();
64
+
61
65
  return this.stateStore;
62
66
  }
63
67
 
@@ -98,8 +102,13 @@ export class StatePart<TStatePartName, TStatePayload> {
98
102
  * creates a cumulative notification by adding a change notification at the end of the call stack;
99
103
  */
100
104
  public notifyChangeCumulative() {
101
- // TODO: check viability
102
- setTimeout(async () => {
105
+ // Debounce: clear any pending notification
106
+ if (this.pendingCumulativeNotification) {
107
+ clearTimeout(this.pendingCumulativeNotification);
108
+ }
109
+
110
+ this.pendingCumulativeNotification = setTimeout(async () => {
111
+ this.pendingCumulativeNotification = null;
103
112
  if (this.stateStore) {
104
113
  await this.notifyChange();
105
114
  }
@@ -122,7 +131,8 @@ export class StatePart<TStatePartName, TStatePayload> {
122
131
  try {
123
132
  return selectorFn(stateArg);
124
133
  } catch (e) {
125
- // Nothing here
134
+ console.error(`Selector error in state part '${this.name}':`, e);
135
+ return undefined;
126
136
  }
127
137
  })
128
138
  );
@@ -151,20 +161,41 @@ export class StatePart<TStatePartName, TStatePayload> {
151
161
  /**
152
162
  * waits until a certain part of the state becomes available
153
163
  * @param selectorFn
164
+ * @param timeoutMs - optional timeout in milliseconds to prevent indefinite waiting
154
165
  */
155
166
  public async waitUntilPresent<T = TStatePayload>(
156
- selectorFn?: (state: TStatePayload) => T
167
+ selectorFn?: (state: TStatePayload) => T,
168
+ timeoutMs?: number
157
169
  ): Promise<T> {
158
170
  const done = plugins.smartpromise.defer<T>();
159
171
  const selectedObservable = this.select(selectorFn);
160
- const subscription = selectedObservable.subscribe(async (value) => {
161
- if (value) {
172
+ let resolved = false;
173
+
174
+ const subscription = selectedObservable.subscribe((value) => {
175
+ if (value && !resolved) {
176
+ resolved = true;
162
177
  done.resolve(value);
163
178
  }
164
179
  });
165
- const result = await done.promise;
166
- subscription.unsubscribe();
167
- return result;
180
+
181
+ let timeoutId: ReturnType<typeof setTimeout> | undefined;
182
+ if (timeoutMs) {
183
+ timeoutId = setTimeout(() => {
184
+ if (!resolved) {
185
+ resolved = true;
186
+ subscription.unsubscribe();
187
+ done.reject(new Error(`waitUntilPresent timed out after ${timeoutMs}ms`));
188
+ }
189
+ }, timeoutMs);
190
+ }
191
+
192
+ try {
193
+ const result = await done.promise;
194
+ return result;
195
+ } finally {
196
+ subscription.unsubscribe();
197
+ if (timeoutId) clearTimeout(timeoutId);
198
+ }
168
199
  }
169
200
 
170
201
  /**
@@ -175,6 +206,6 @@ export class StatePart<TStatePartName, TStatePayload> {
175
206
  ) {
176
207
  const resultPromise = funcArg(this);
177
208
  this.cumulativeDeferred.addPromise(resultPromise);
178
- this.setState(await resultPromise);
209
+ await this.setState(await resultPromise);
179
210
  }
180
211
  }
@@ -1,8 +0,0 @@
1
- import { StatePart } from './smartstate.classes.statepart';
2
- /**
3
- * A StatePartCollection is a collection of StateParts.
4
- * It can be used for expressing interest in a certain set of StateParts.
5
- */
6
- export declare class StatePartCollection<StatePartNameType, T> extends StatePart<StatePartNameType, T> {
7
- constructor(nameArg: StatePartNameType);
8
- }
@@ -1,15 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.StatePartCollection = void 0;
4
- const smartstate_classes_statepart_1 = require("./smartstate.classes.statepart");
5
- /**
6
- * A StatePartCollection is a collection of StateParts.
7
- * It can be used for expressing interest in a certain set of StateParts.
8
- */
9
- class StatePartCollection extends smartstate_classes_statepart_1.StatePart {
10
- constructor(nameArg) {
11
- super(nameArg);
12
- }
13
- }
14
- exports.StatePartCollection = StatePartCollection;
15
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRzdGF0ZS5jbGFzc2VzLnN0YXRlY29sbGVjdGlvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYXJ0c3RhdGUuY2xhc3Nlcy5zdGF0ZWNvbGxlY3Rpb24udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQ0EsaUZBQTJEO0FBRTNEOzs7R0FHRztBQUNILE1BQWEsbUJBQTBDLFNBQVEsd0NBQStCO0lBQzVGLFlBQVksT0FBMEI7UUFDcEMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQ2pCLENBQUM7Q0FDRjtBQUpELGtEQUlDIn0=
@@ -1,10 +0,0 @@
1
- /**
2
- * State observable observes a StatePart and notifies everyone interested
3
- */
4
- export declare class StateObservable {
5
- /**
6
- * creates an observable from a StateCollection
7
- */
8
- static fromStatePartCollection(filterArg?: () => any): void;
9
- constructor();
10
- }
@@ -1,15 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.StateObservable = void 0;
4
- /**
5
- * State observable observes a StatePart and notifies everyone interested
6
- */
7
- class StateObservable {
8
- /**
9
- * creates an observable from a StateCollection
10
- */
11
- static fromStatePartCollection(filterArg) { }
12
- constructor() { }
13
- }
14
- exports.StateObservable = StateObservable;
15
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRzdGF0ZS5jbGFzc2VzLnN0YXRlb2JzZXJ2YWJsZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYXJ0c3RhdGUuY2xhc3Nlcy5zdGF0ZW9ic2VydmFibGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBRUE7O0dBRUc7QUFDSCxNQUFhLGVBQWU7SUFDMUI7O09BRUc7SUFDSSxNQUFNLENBQUMsdUJBQXVCLENBQUMsU0FBcUIsSUFBRyxDQUFDO0lBRS9ELGdCQUFlLENBQUM7Q0FDakI7QUFQRCwwQ0FPQyJ9