@push.rocks/smartstate 2.0.31 → 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.0.31',
7
- description: 'A package for handling and managing state in applications.'
6
+ version: '2.1.1',
7
+ description: 'A TypeScript-first reactive state management library with middleware, computed state, batching, persistence, and Web Component Context Protocol support.'
8
8
  };
9
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx3QkFBd0I7SUFDOUIsT0FBTyxFQUFFLFFBQVE7SUFDakIsV0FBVyxFQUFFLDREQUE0RDtDQUMxRSxDQUFBIn0=
9
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx3QkFBd0I7SUFDOUIsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLDBKQUEwSjtDQUN4SyxDQUFBIn0=
@@ -1,3 +1,5 @@
1
1
  export * from './smartstate.classes.smartstate.js';
2
2
  export * from './smartstate.classes.statepart.js';
3
3
  export * from './smartstate.classes.stateaction.js';
4
+ export * from './smartstate.classes.computed.js';
5
+ export * from './smartstate.contextprovider.js';
package/dist_ts/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  export * from './smartstate.classes.smartstate.js';
2
2
  export * from './smartstate.classes.statepart.js';
3
3
  export * from './smartstate.classes.stateaction.js';
4
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLG9DQUFvQyxDQUFDO0FBQ25ELGNBQWMsbUNBQW1DLENBQUM7QUFDbEQsY0FBYyxxQ0FBcUMsQ0FBQyJ9
4
+ export * from './smartstate.classes.computed.js';
5
+ export * from './smartstate.contextprovider.js';
6
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLG9DQUFvQyxDQUFDO0FBQ25ELGNBQWMsbUNBQW1DLENBQUM7QUFDbEQsY0FBYyxxQ0FBcUMsQ0FBQztBQUNwRCxjQUFjLGtDQUFrQyxDQUFDO0FBQ2pELGNBQWMsaUNBQWlDLENBQUMifQ==
@@ -0,0 +1,7 @@
1
+ import * as plugins from './smartstate.plugins.js';
2
+ import type { StatePart } from './smartstate.classes.statepart.js';
3
+ /**
4
+ * creates a computed observable derived from multiple state parts.
5
+ * the observable is lazy — it only subscribes to sources when subscribed to.
6
+ */
7
+ export declare function computed<TResult>(sources: StatePart<any, any>[], computeFn: (...states: any[]) => TResult): plugins.smartrx.rxjs.Observable<TResult>;
@@ -0,0 +1,10 @@
1
+ import * as plugins from './smartstate.plugins.js';
2
+ import { combineLatest, map } from 'rxjs';
3
+ /**
4
+ * creates a computed observable derived from multiple state parts.
5
+ * the observable is lazy — it only subscribes to sources when subscribed to.
6
+ */
7
+ export function computed(sources, computeFn) {
8
+ return combineLatest(sources.map((sp) => sp.select())).pipe(map((states) => computeFn(...states)));
9
+ }
10
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRzdGF0ZS5jbGFzc2VzLmNvbXB1dGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc21hcnRzdGF0ZS5jbGFzc2VzLmNvbXB1dGVkLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0seUJBQXlCLENBQUM7QUFDbkQsT0FBTyxFQUFFLGFBQWEsRUFBRSxHQUFHLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFHMUM7OztHQUdHO0FBQ0gsTUFBTSxVQUFVLFFBQVEsQ0FDdEIsT0FBOEIsRUFDOUIsU0FBd0M7SUFFeEMsT0FBTyxhQUFhLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQ3pELEdBQUcsQ0FBQyxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsU0FBUyxDQUFDLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FDTSxDQUFDO0FBQ2hELENBQUMifQ==
@@ -1,3 +1,4 @@
1
+ import * as plugins from './smartstate.plugins.js';
1
2
  import { StatePart } from './smartstate.classes.statepart.js';
2
3
  export type TInitMode = 'soft' | 'mandatory' | 'force' | 'persistent';
3
4
  /**
@@ -8,23 +9,32 @@ export declare class Smartstate<StatePartNameType extends string> {
8
9
  [key in StatePartNameType]?: StatePart<StatePartNameType, any>;
9
10
  };
10
11
  private pendingStatePartCreation;
12
+ private batchDepth;
13
+ private isFlushing;
14
+ private pendingNotifications;
11
15
  constructor();
16
+ /**
17
+ * whether state changes are currently being batched
18
+ */
19
+ get isBatching(): boolean;
20
+ /**
21
+ * registers a state part for deferred notification during a batch
22
+ */
23
+ registerPendingNotification(statePart: StatePart<any, any>): void;
24
+ /**
25
+ * batches multiple state updates so subscribers are only notified once all updates complete
26
+ */
27
+ batch(updateFn: () => Promise<void> | void): Promise<void>;
28
+ /**
29
+ * creates a computed observable derived from multiple state parts
30
+ */
31
+ computed<TResult>(sources: StatePart<StatePartNameType, any>[], computeFn: (...states: any[]) => TResult): plugins.smartrx.rxjs.Observable<TResult>;
12
32
  /**
13
33
  * Allows getting and initializing a new statepart
14
- * initMode === 'soft' (default) - returns existing statepart if exists, creates new if not
15
- * initMode === 'mandatory' - requires statepart to not exist, fails if it does
16
- * initMode === 'force' - always creates new statepart, overwriting any existing
17
- * initMode === 'persistent' - like 'soft' but with webstore persistence
18
- * @param statePartNameArg
19
- * @param initialArg
20
- * @param initMode
21
34
  */
22
35
  getStatePart<PayloadType>(statePartNameArg: StatePartNameType, initialArg?: PayloadType, initMode?: TInitMode): Promise<StatePart<StatePartNameType, PayloadType>>;
23
36
  /**
24
37
  * Creates a statepart
25
- * @param statePartName
26
- * @param initialPayloadArg
27
- * @param initMode
28
38
  */
29
39
  private createStatePart;
30
40
  }
@@ -1,5 +1,6 @@
1
1
  import * as plugins from './smartstate.plugins.js';
2
2
  import { StatePart } from './smartstate.classes.statepart.js';
3
+ import { computed } from './smartstate.classes.computed.js';
3
4
  /**
4
5
  * Smartstate takes care of providing state
5
6
  */
@@ -7,16 +8,58 @@ export class Smartstate {
7
8
  constructor() {
8
9
  this.statePartMap = {};
9
10
  this.pendingStatePartCreation = new Map();
11
+ // Batch support
12
+ this.batchDepth = 0;
13
+ this.isFlushing = false;
14
+ this.pendingNotifications = new Set();
15
+ }
16
+ /**
17
+ * whether state changes are currently being batched
18
+ */
19
+ get isBatching() {
20
+ return this.batchDepth > 0;
21
+ }
22
+ /**
23
+ * registers a state part for deferred notification during a batch
24
+ */
25
+ registerPendingNotification(statePart) {
26
+ this.pendingNotifications.add(statePart);
27
+ }
28
+ /**
29
+ * batches multiple state updates so subscribers are only notified once all updates complete
30
+ */
31
+ async batch(updateFn) {
32
+ this.batchDepth++;
33
+ try {
34
+ await updateFn();
35
+ }
36
+ finally {
37
+ this.batchDepth--;
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;
51
+ }
52
+ }
53
+ }
54
+ }
55
+ /**
56
+ * creates a computed observable derived from multiple state parts
57
+ */
58
+ computed(sources, computeFn) {
59
+ return computed(sources, computeFn);
10
60
  }
11
61
  /**
12
62
  * Allows getting and initializing a new statepart
13
- * initMode === 'soft' (default) - returns existing statepart if exists, creates new if not
14
- * initMode === 'mandatory' - requires statepart to not exist, fails if it does
15
- * initMode === 'force' - always creates new statepart, overwriting any existing
16
- * initMode === 'persistent' - like 'soft' but with webstore persistence
17
- * @param statePartNameArg
18
- * @param initialArg
19
- * @param initMode
20
63
  */
21
64
  async getStatePart(statePartNameArg, initialArg, initMode = 'soft') {
22
65
  // Return pending creation if one exists to prevent duplicate state parts
@@ -30,18 +73,16 @@ export class Smartstate {
30
73
  case 'mandatory':
31
74
  throw new Error(`State part '${statePartNameArg}' already exists, but initMode is 'mandatory'`);
32
75
  case 'force':
33
- // Force mode: create new state part
34
- break; // Fall through to creation
76
+ existingStatePart.dispose();
77
+ break;
35
78
  case 'soft':
36
79
  case 'persistent':
37
80
  default:
38
- // Return existing state part
39
81
  return existingStatePart;
40
82
  }
41
83
  }
42
84
  else {
43
- // State part doesn't exist
44
- if (!initialArg) {
85
+ if (initialArg === undefined) {
45
86
  throw new Error(`State part '${statePartNameArg}' does not exist and no initial state provided`);
46
87
  }
47
88
  }
@@ -57,9 +98,6 @@ export class Smartstate {
57
98
  }
58
99
  /**
59
100
  * Creates a statepart
60
- * @param statePartName
61
- * @param initialPayloadArg
62
- * @param initMode
63
101
  */
64
102
  async createStatePart(statePartName, initialPayloadArg, initMode = 'soft') {
65
103
  const newState = new StatePart(statePartName, initMode === 'persistent'
@@ -68,21 +106,20 @@ export class Smartstate {
68
106
  storeName: statePartName,
69
107
  }
70
108
  : null);
109
+ newState.smartstateRef = this;
71
110
  await newState.init();
72
111
  const currentState = newState.getState();
73
112
  if (initMode === 'persistent' && currentState !== undefined) {
74
- // Persisted state exists - merge with defaults, persisted values take precedence
75
113
  await newState.setState({
76
114
  ...initialPayloadArg,
77
115
  ...currentState,
78
116
  });
79
117
  }
80
118
  else {
81
- // No persisted state or non-persistent mode
82
119
  await newState.setState(initialPayloadArg);
83
120
  }
84
121
  this.statePartMap[statePartName] = newState;
85
122
  return newState;
86
123
  }
87
124
  }
88
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRzdGF0ZS5jbGFzc2VzLnNtYXJ0c3RhdGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydHN0YXRlLmNsYXNzZXMuc21hcnRzdGF0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHlCQUF5QixDQUFDO0FBQ25ELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUk5RDs7R0FFRztBQUNILE1BQU0sT0FBTyxVQUFVO0lBS3JCO1FBSk8saUJBQVksR0FBdUUsRUFBRSxDQUFDO1FBRXJGLDZCQUF3QixHQUE0RCxJQUFJLEdBQUcsRUFBRSxDQUFDO0lBRXZGLENBQUM7SUFFaEI7Ozs7Ozs7OztPQVNHO0lBQ0ksS0FBSyxDQUFDLFlBQVksQ0FDdkIsZ0JBQW1DLEVBQ25DLFVBQXdCLEVBQ3hCLFdBQXNCLE1BQU07UUFFNUIseUVBQXlFO1FBQ3pFLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyx3QkFBd0IsQ0FBQyxHQUFHLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztRQUNwRSxJQUFJLE9BQU8sRUFBRSxDQUFDO1lBQ1osT0FBTyxPQUE2RCxDQUFDO1FBQ3ZFLENBQUM7UUFFRCxNQUFNLGlCQUFpQixHQUFHLElBQUksQ0FBQyxZQUFZLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztRQUU5RCxJQUFJLGlCQUFpQixFQUFFLENBQUM7WUFDdEIsUUFBUSxRQUFRLEVBQUUsQ0FBQztnQkFDakIsS0FBSyxXQUFXO29CQUNkLE1BQU0sSUFBSSxLQUFLLENBQ2IsZUFBZSxnQkFBZ0IsK0NBQStDLENBQy9FLENBQUM7Z0JBQ0osS0FBSyxPQUFPO29CQUNWLG9DQUFvQztvQkFDcEMsTUFBTSxDQUFDLDJCQUEyQjtnQkFDcEMsS0FBSyxNQUFNLENBQUM7Z0JBQ1osS0FBSyxZQUFZLENBQUM7Z0JBQ2xCO29CQUNFLDZCQUE2QjtvQkFDN0IsT0FBTyxpQkFBOEQsQ0FBQztZQUMxRSxDQUFDO1FBQ0gsQ0FBQzthQUFNLENBQUM7WUFDTiwyQkFBMkI7WUFDM0IsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO2dCQUNoQixNQUFNLElBQUksS0FBSyxDQUNiLGVBQWUsZ0JBQWdCLGdEQUFnRCxDQUNoRixDQUFDO1lBQ0osQ0FBQztRQUNILENBQUM7UUFFRCxNQUFNLGVBQWUsR0FBRyxJQUFJLENBQUMsZUFBZSxDQUFjLGdCQUFnQixFQUFFLFVBQVUsRUFBRSxRQUFRLENBQUMsQ0FBQztRQUNsRyxJQUFJLENBQUMsd0JBQXdCLENBQUMsR0FBRyxDQUFDLGdCQUFnQixFQUFFLGVBQWUsQ0FBQyxDQUFDO1FBRXJFLElBQUksQ0FBQztZQUNILE1BQU0sTUFBTSxHQUFHLE1BQU0sZUFBZSxDQUFDO1lBQ3JDLE9BQU8sTUFBTSxDQUFDO1FBQ2hCLENBQUM7Z0JBQVMsQ0FBQztZQUNULElBQUksQ0FBQyx3QkFBd0IsQ0FBQyxNQUFNLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztRQUN6RCxDQUFDO0lBQ0gsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0ssS0FBSyxDQUFDLGVBQWUsQ0FDM0IsYUFBZ0MsRUFDaEMsaUJBQThCLEVBQzlCLFdBQXNCLE1BQU07UUFFNUIsTUFBTSxRQUFRLEdBQUcsSUFBSSxTQUFTLENBQzVCLGFBQWEsRUFDYixRQUFRLEtBQUssWUFBWTtZQUN2QixDQUFDLENBQUM7Z0JBQ0UsTUFBTSxFQUFFLFlBQVk7Z0JBQ3BCLFNBQVMsRUFBRSxhQUFhO2FBQ3pCO1lBQ0gsQ0FBQyxDQUFDLElBQUksQ0FDVCxDQUFDO1FBQ0YsTUFBTSxRQUFRLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDdEIsTUFBTSxZQUFZLEdBQUcsUUFBUSxDQUFDLFFBQVEsRUFBRSxDQUFDO1FBRXpDLElBQUksUUFBUSxLQUFLLFlBQVksSUFBSSxZQUFZLEtBQUssU0FBUyxFQUFFLENBQUM7WUFDNUQsaUZBQWlGO1lBQ2pGLE1BQU0sUUFBUSxDQUFDLFFBQVEsQ0FBQztnQkFDdEIsR0FBRyxpQkFBaUI7Z0JBQ3BCLEdBQUcsWUFBWTthQUNoQixDQUFDLENBQUM7UUFDTCxDQUFDO2FBQU0sQ0FBQztZQUNOLDRDQUE0QztZQUM1QyxNQUFNLFFBQVEsQ0FBQyxRQUFRLENBQUMsaUJBQWlCLENBQUMsQ0FBQztRQUM3QyxDQUFDO1FBRUQsSUFBSSxDQUFDLFlBQVksQ0FBQyxhQUFhLENBQUMsR0FBRyxRQUFRLENBQUM7UUFDNUMsT0FBTyxRQUFRLENBQUM7SUFDbEIsQ0FBQztDQUNGIn0=
125
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRzdGF0ZS5jbGFzc2VzLnNtYXJ0c3RhdGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydHN0YXRlLmNsYXNzZXMuc21hcnRzdGF0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHlCQUF5QixDQUFDO0FBQ25ELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUM5RCxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sa0NBQWtDLENBQUM7QUFJNUQ7O0dBRUc7QUFDSCxNQUFNLE9BQU8sVUFBVTtJQVVyQjtRQVRPLGlCQUFZLEdBQXVFLEVBQUUsQ0FBQztRQUVyRiw2QkFBd0IsR0FBNEQsSUFBSSxHQUFHLEVBQUUsQ0FBQztRQUV0RyxnQkFBZ0I7UUFDUixlQUFVLEdBQUcsQ0FBQyxDQUFDO1FBQ2YsZUFBVSxHQUFHLEtBQUssQ0FBQztRQUNuQix5QkFBb0IsR0FBRyxJQUFJLEdBQUcsRUFBdUIsQ0FBQztJQUUvQyxDQUFDO0lBRWhCOztPQUVHO0lBQ0gsSUFBVyxVQUFVO1FBQ25CLE9BQU8sSUFBSSxDQUFDLFVBQVUsR0FBRyxDQUFDLENBQUM7SUFDN0IsQ0FBQztJQUVEOztPQUVHO0lBQ0ksMkJBQTJCLENBQUMsU0FBOEI7UUFDL0QsSUFBSSxDQUFDLG9CQUFvQixDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUMzQyxDQUFDO0lBRUQ7O09BRUc7SUFDSSxLQUFLLENBQUMsS0FBSyxDQUFDLFFBQW9DO1FBQ3JELElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztRQUNsQixJQUFJLENBQUM7WUFDSCxNQUFNLFFBQVEsRUFBRSxDQUFDO1FBQ25CLENBQUM7Z0JBQVMsQ0FBQztZQUNULElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztZQUNsQixJQUFJLElBQUksQ0FBQyxVQUFVLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFD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
@@ -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=
@@ -1,13 +1,20 @@
1
1
  import * as plugins from './smartstate.plugins.js';
2
2
  import { StateAction, type IActionDef } from './smartstate.classes.stateaction.js';
3
+ import type { Smartstate } from './smartstate.classes.smartstate.js';
4
+ export type TMiddleware<TPayload> = (newState: TPayload, oldState: TPayload | undefined) => TPayload | Promise<TPayload>;
3
5
  export declare class StatePart<TStatePartName, TStatePayload> {
4
6
  name: TStatePartName;
5
7
  state: plugins.smartrx.rxjs.Subject<TStatePayload>;
6
8
  stateStore: TStatePayload | undefined;
9
+ smartstateRef?: Smartstate<any>;
7
10
  private cumulativeDeferred;
11
+ private mutationQueue;
8
12
  private pendingCumulativeNotification;
9
13
  private webStoreOptions;
10
14
  private webStore;
15
+ private middlewares;
16
+ private selectorCache;
17
+ private defaultSelectObservable;
11
18
  constructor(nameArg: TStatePartName, webStoreOptionsArg?: plugins.webstore.IWebStoreOptions);
12
19
  /**
13
20
  * initializes the webstore
@@ -18,13 +25,21 @@ export declare class StatePart<TStatePartName, TStatePayload> {
18
25
  */
19
26
  getState(): TStatePayload | undefined;
20
27
  /**
21
- * sets the stateStore to the new state
22
- * @param newStateArg
28
+ * adds a middleware that intercepts setState calls.
29
+ * middleware can transform the state or throw to reject it.
30
+ * returns a removal function.
31
+ */
32
+ addMiddleware(middleware: TMiddleware<TStatePayload>): () => void;
33
+ /**
34
+ * sets the stateStore to the new state (serialized via mutation queue)
23
35
  */
24
36
  setState(newStateArg: TStatePayload): Promise<TStatePayload>;
37
+ /**
38
+ * applies the state change (middleware → validate → persist → notify)
39
+ */
40
+ private applyState;
25
41
  /**
26
42
  * Validates state structure - can be overridden for custom validation
27
- * @param stateArg
28
43
  */
29
44
  protected validateState(stateArg: any): stateArg is TStatePayload;
30
45
  /**
@@ -33,13 +48,17 @@ export declare class StatePart<TStatePartName, TStatePayload> {
33
48
  notifyChange(): Promise<void>;
34
49
  private lastStateNotificationPayloadHash;
35
50
  /**
36
- * creates a cumulative notification by adding a change notification at the end of the call stack;
51
+ * creates a cumulative notification by adding a change notification at the end of the call stack
37
52
  */
38
53
  notifyChangeCumulative(): void;
39
54
  /**
40
- * selects a state or a substate
55
+ * selects a state or a substate.
56
+ * supports an optional AbortSignal for automatic unsubscription.
57
+ * memoizes observables by selector function reference.
41
58
  */
42
- select<T = TStatePayload>(selectorFn?: (state: TStatePayload) => T): plugins.smartrx.rxjs.Observable<T>;
59
+ select<T = TStatePayload>(selectorFn?: (state: TStatePayload) => T, options?: {
60
+ signal?: AbortSignal;
61
+ }): plugins.smartrx.rxjs.Observable<T>;
43
62
  /**
44
63
  * creates an action capable of modifying the state
45
64
  */
@@ -49,13 +68,19 @@ export declare class StatePart<TStatePartName, TStatePayload> {
49
68
  */
50
69
  dispatchAction<T>(stateAction: StateAction<TStatePayload, T>, actionPayload: T): Promise<TStatePayload>;
51
70
  /**
52
- * waits until a certain part of the state becomes available
53
- * @param selectorFn
54
- * @param timeoutMs - optional timeout in milliseconds to prevent indefinite waiting
71
+ * waits until a certain part of the state becomes available.
72
+ * supports optional timeout and AbortSignal.
55
73
  */
56
- waitUntilPresent<T = TStatePayload>(selectorFn?: (state: TStatePayload) => T, timeoutMs?: number): Promise<T>;
74
+ waitUntilPresent<T = TStatePayload>(selectorFn?: (state: TStatePayload) => T, optionsOrTimeout?: number | {
75
+ timeoutMs?: number;
76
+ signal?: AbortSignal;
77
+ }): Promise<T>;
57
78
  /**
58
79
  * is executed
59
80
  */
60
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;
61
86
  }