@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.
@@ -1,13 +1,36 @@
1
1
  import * as plugins from './smartstate.plugins.js';
2
+ import { Observable, shareReplay, takeUntil } from 'rxjs';
2
3
  import { StateAction } from './smartstate.classes.stateaction.js';
4
+ /**
5
+ * creates an Observable that emits once when the given AbortSignal fires
6
+ */
7
+ function fromAbortSignal(signal) {
8
+ return new Observable((subscriber) => {
9
+ if (signal.aborted) {
10
+ subscriber.next();
11
+ subscriber.complete();
12
+ return;
13
+ }
14
+ const handler = () => {
15
+ subscriber.next();
16
+ subscriber.complete();
17
+ };
18
+ signal.addEventListener('abort', handler);
19
+ return () => signal.removeEventListener('abort', handler);
20
+ });
21
+ }
3
22
  export class StatePart {
4
23
  constructor(nameArg, webStoreOptionsArg) {
5
24
  this.state = new plugins.smartrx.rxjs.Subject();
6
25
  this.cumulativeDeferred = plugins.smartpromise.cumulativeDefer();
26
+ this.mutationQueue = Promise.resolve();
7
27
  this.pendingCumulativeNotification = null;
8
- this.webStore = null; // Add WebStore instance
28
+ this.webStore = null;
29
+ this.middlewares = [];
30
+ // Selector memoization
31
+ this.selectorCache = new WeakMap();
32
+ this.defaultSelectObservable = null;
9
33
  this.name = nameArg;
10
- // Initialize WebStore if webStoreOptions are provided
11
34
  if (webStoreOptionsArg) {
12
35
  this.webStoreOptions = webStoreOptionsArg;
13
36
  }
@@ -33,84 +56,139 @@ export class StatePart {
33
56
  return this.stateStore;
34
57
  }
35
58
  /**
36
- * sets the stateStore to the new state
37
- * @param newStateArg
59
+ * adds a middleware that intercepts setState calls.
60
+ * middleware can transform the state or throw to reject it.
61
+ * returns a removal function.
62
+ */
63
+ addMiddleware(middleware) {
64
+ this.middlewares.push(middleware);
65
+ return () => {
66
+ const idx = this.middlewares.indexOf(middleware);
67
+ if (idx !== -1) {
68
+ this.middlewares.splice(idx, 1);
69
+ }
70
+ };
71
+ }
72
+ /**
73
+ * sets the stateStore to the new state (serialized via mutation queue)
38
74
  */
39
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) {
82
+ // Run middleware chain
83
+ let processedState = newStateArg;
84
+ for (const mw of this.middlewares) {
85
+ processedState = await mw(processedState, this.stateStore);
86
+ }
40
87
  // Validate state structure
41
- if (!this.validateState(newStateArg)) {
88
+ if (!this.validateState(processedState)) {
42
89
  throw new Error(`Invalid state structure for state part '${this.name}'`);
43
90
  }
44
- // Save to WebStore first to ensure atomicity - if save fails, memory state remains unchanged
91
+ // Save to WebStore first to ensure atomicity
45
92
  if (this.webStore) {
46
- await this.webStore.set(String(this.name), newStateArg);
93
+ await this.webStore.set(String(this.name), processedState);
47
94
  }
48
95
  // Update in-memory state after successful persistence
49
- this.stateStore = newStateArg;
96
+ this.stateStore = processedState;
50
97
  await this.notifyChange();
51
98
  return this.stateStore;
52
99
  }
53
100
  /**
54
101
  * Validates state structure - can be overridden for custom validation
55
- * @param stateArg
56
102
  */
57
103
  validateState(stateArg) {
58
- // Basic validation - ensure state is not null/undefined
59
- // Subclasses can override for more specific validation
60
104
  return stateArg !== null && stateArg !== undefined;
61
105
  }
62
106
  /**
63
107
  * notifies of a change on the state
64
108
  */
65
109
  async notifyChange() {
66
- if (!this.stateStore) {
110
+ const snapshot = this.stateStore;
111
+ if (snapshot === undefined) {
112
+ return;
113
+ }
114
+ // If inside a batch, defer the notification
115
+ if (this.smartstateRef?.isBatching) {
116
+ this.smartstateRef.registerPendingNotification(this);
67
117
  return;
68
118
  }
69
119
  const createStateHash = async (stateArg) => {
70
120
  return await plugins.smarthashWeb.sha256FromString(plugins.smartjson.stableOneWayStringify(stateArg));
71
121
  };
72
- const currentHash = await createStateHash(this.stateStore);
73
- if (this.lastStateNotificationPayloadHash &&
74
- currentHash === this.lastStateNotificationPayloadHash) {
75
- return;
76
- }
77
- else {
122
+ try {
123
+ const currentHash = await createStateHash(snapshot);
124
+ if (this.lastStateNotificationPayloadHash &&
125
+ currentHash === this.lastStateNotificationPayloadHash) {
126
+ return;
127
+ }
78
128
  this.lastStateNotificationPayloadHash = currentHash;
79
129
  }
80
- 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);
81
134
  }
82
135
  /**
83
- * creates a cumulative notification by adding a change notification at the end of the call stack;
136
+ * creates a cumulative notification by adding a change notification at the end of the call stack
84
137
  */
85
138
  notifyChangeCumulative() {
86
- // Debounce: clear any pending notification
87
139
  if (this.pendingCumulativeNotification) {
88
140
  clearTimeout(this.pendingCumulativeNotification);
89
141
  }
90
- this.pendingCumulativeNotification = setTimeout(async () => {
142
+ this.pendingCumulativeNotification = setTimeout(() => {
91
143
  this.pendingCumulativeNotification = null;
92
- if (this.stateStore) {
93
- await this.notifyChange();
144
+ if (this.stateStore !== undefined) {
145
+ this.notifyChange().catch((err) => {
146
+ console.error(`notifyChangeCumulative failed for '${this.name}':`, err);
147
+ });
94
148
  }
95
149
  }, 0);
96
150
  }
97
151
  /**
98
- * selects a state or a substate
152
+ * selects a state or a substate.
153
+ * supports an optional AbortSignal for automatic unsubscription.
154
+ * memoizes observables by selector function reference.
99
155
  */
100
- select(selectorFn) {
101
- if (!selectorFn) {
102
- selectorFn = (state) => state;
156
+ select(selectorFn, options) {
157
+ const hasSignal = options?.signal != null;
158
+ // Check memoization cache (only for non-signal selects)
159
+ if (!hasSignal) {
160
+ if (!selectorFn) {
161
+ if (this.defaultSelectObservable) {
162
+ return this.defaultSelectObservable;
163
+ }
164
+ }
165
+ else if (this.selectorCache.has(selectorFn)) {
166
+ return this.selectorCache.get(selectorFn);
167
+ }
103
168
  }
104
- const mapped = this.state.pipe(plugins.smartrx.rxjs.ops.startWith(this.getState()), plugins.smartrx.rxjs.ops.filter((stateArg) => stateArg !== undefined), plugins.smartrx.rxjs.ops.map((stateArg) => {
169
+ const effectiveSelectorFn = selectorFn || ((state) => state);
170
+ let mapped = this.state.pipe(plugins.smartrx.rxjs.ops.startWith(this.getState()), plugins.smartrx.rxjs.ops.filter((stateArg) => stateArg !== undefined), plugins.smartrx.rxjs.ops.map((stateArg) => {
105
171
  try {
106
- return selectorFn(stateArg);
172
+ return effectiveSelectorFn(stateArg);
107
173
  }
108
174
  catch (e) {
109
175
  console.error(`Selector error in state part '${this.name}':`, e);
110
176
  return undefined;
111
177
  }
112
178
  }));
113
- return mapped;
179
+ if (hasSignal) {
180
+ mapped = mapped.pipe(takeUntil(fromAbortSignal(options.signal)));
181
+ return mapped;
182
+ }
183
+ // Apply shareReplay for caching and store in memo cache
184
+ const shared = mapped.pipe(shareReplay({ bufferSize: 1, refCount: true }));
185
+ if (!selectorFn) {
186
+ this.defaultSelectObservable = shared;
187
+ }
188
+ else {
189
+ this.selectorCache.set(selectorFn, shared);
190
+ }
191
+ return shared;
114
192
  }
115
193
  /**
116
194
  * creates an action capable of modifying the state
@@ -123,21 +201,38 @@ export class StatePart {
123
201
  */
124
202
  async dispatchAction(stateAction, actionPayload) {
125
203
  await this.cumulativeDeferred.promise;
126
- const newState = await stateAction.actionDef(this, actionPayload);
127
- await this.setState(newState);
128
- 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
+ });
129
211
  }
130
212
  /**
131
- * waits until a certain part of the state becomes available
132
- * @param selectorFn
133
- * @param timeoutMs - optional timeout in milliseconds to prevent indefinite waiting
213
+ * waits until a certain part of the state becomes available.
214
+ * supports optional timeout and AbortSignal.
134
215
  */
135
- async waitUntilPresent(selectorFn, timeoutMs) {
216
+ async waitUntilPresent(selectorFn, optionsOrTimeout) {
217
+ // Parse backward-compatible args
218
+ let timeoutMs;
219
+ let signal;
220
+ if (typeof optionsOrTimeout === 'number') {
221
+ timeoutMs = optionsOrTimeout;
222
+ }
223
+ else if (optionsOrTimeout) {
224
+ timeoutMs = optionsOrTimeout.timeoutMs;
225
+ signal = optionsOrTimeout.signal;
226
+ }
136
227
  const done = plugins.smartpromise.defer();
137
228
  const selectedObservable = this.select(selectorFn);
138
229
  let resolved = false;
230
+ // Check if already aborted
231
+ if (signal?.aborted) {
232
+ throw new Error('Aborted');
233
+ }
139
234
  const subscription = selectedObservable.subscribe((value) => {
140
- if (value && !resolved) {
235
+ if (value !== undefined && value !== null && !resolved) {
141
236
  resolved = true;
142
237
  done.resolve(value);
143
238
  }
@@ -152,6 +247,19 @@ export class StatePart {
152
247
  }
153
248
  }, timeoutMs);
154
249
  }
250
+ // Handle abort signal
251
+ const abortHandler = signal ? () => {
252
+ if (!resolved) {
253
+ resolved = true;
254
+ subscription.unsubscribe();
255
+ if (timeoutId)
256
+ clearTimeout(timeoutId);
257
+ done.reject(new Error('Aborted'));
258
+ }
259
+ } : undefined;
260
+ if (signal && abortHandler) {
261
+ signal.addEventListener('abort', abortHandler);
262
+ }
155
263
  try {
156
264
  const result = await done.promise;
157
265
  return result;
@@ -160,6 +268,9 @@ export class StatePart {
160
268
  subscription.unsubscribe();
161
269
  if (timeoutId)
162
270
  clearTimeout(timeoutId);
271
+ if (signal && abortHandler) {
272
+ signal.removeEventListener('abort', abortHandler);
273
+ }
163
274
  }
164
275
  }
165
276
  /**
@@ -170,5 +281,20 @@ export class StatePart {
170
281
  this.cumulativeDeferred.addPromise(resultPromise);
171
282
  await this.setState(await resultPromise);
172
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
+ }
173
299
  }
174
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRzdGF0ZS5jbGFzc2VzLnN0YXRlcGFydC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYXJ0c3RhdGUuY2xhc3Nlcy5zdGF0ZXBhcnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSx5QkFBeUIsQ0FBQztBQUNuRCxPQUFPLEVBQUUsV0FBVyxFQUFtQixNQUFNLHFDQUFxQyxDQUFDO0FBRW5GLE1BQU0sT0FBTyxTQUFTO0lBV3BCLFlBQVksT0FBdUIsRUFBRSxrQkFBc0Q7UUFUcEYsVUFBSyxHQUFHLElBQUksT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFpQixDQUFDO1FBRXpELHVCQUFrQixHQUFHLE9BQU8sQ0FBQyxZQUFZLENBQUMsZUFBZSxFQUFFLENBQUM7UUFFNUQsa0NBQTZCLEdBQXlDLElBQUksQ0FBQztRQUczRSxhQUFRLEdBQW9ELElBQUksQ0FBQyxDQUFDLHdCQUF3QjtRQUdoRyxJQUFJLENBQUMsSUFBSSxHQUFHLE9BQU8sQ0FBQztRQUVwQixzREFBc0Q7UUFDdEQsSUFBSSxrQkFBa0IsRUFBRSxDQUFDO1lBQ3ZCLElBQUksQ0FBQyxlQUFlLEdBQUcsa0JBQWtCLENBQUM7UUFDNUMsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxJQUFJO1FBQ2YsSUFBSSxJQUFJLENBQUMsZUFBZSxFQUFFLENBQUM7WUFDekIsSUFBSSxDQUFDLFFBQVEsR0FBRyxJQUFJLE9BQU8sQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFnQixJQUFJLENBQUMsZUFBZSxDQUFDLENBQUM7WUFDbkYsTUFBTSxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSxDQUFDO1lBQzNCLE1BQU0sV0FBVyxHQUFHLE1BQU0sSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1lBQy9E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
300
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRzdGF0ZS5jbGFzc2VzLnN0YXRlcGFydC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYXJ0c3RhdGUuY2xhc3Nlcy5zdGF0ZXBhcnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSx5QkFBeUIsQ0FBQztBQUNuRCxPQUFPLEVBQUUsVUFBVSxFQUFFLFdBQVcsRUFBRSxTQUFTLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDMUQsT0FBTyxFQUFFLFdBQVcsRUFBbUIsTUFBTSxxQ0FBcUMsQ0FBQztBQVFuRjs7R0FFRztBQUNILFNBQVMsZUFBZSxDQUFDLE1BQW1CO0lBQzFDLE9BQU8sSUFBSSxVQUFVLENBQU8sQ0FBQyxVQUFVLEVBQUUsRUFBRTtRQUN6QyxJQUFJLE1BQU0sQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNuQixVQUFVLENBQUMsSUFBSSxFQUFFLENBQUM7WUFDbEIsVUFBVSxDQUFDLFFBQVEsRUFBRSxDQUFDO1lBQ3RCLE9BQU87UUFDVCxDQUFDO1FBQ0QsTUFBTSxPQUFPLEdBQUcsR0FBRyxFQUFFO1lBQ25CLFVBQVUsQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUNsQixVQUFVLENBQUMsUUFBUSxFQUFFLENBQUM7UUFDeEIsQ0FBQyxDQUFDO1FBQ0YsTUFBTSxDQUFDLGdCQUFnQixDQUFDLE9BQU8sRUFBRSxPQUFPLENBQUMsQ0FBQztRQUMxQyxPQUFPLEdBQUcsRUFBRSxDQUFDLE1BQU0sQ0FBQyxtQkFBbUIsQ0FBQyxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDNUQsQ0FBQyxDQUFDLENBQUM7QUFDTCxDQUFDO0FBRUQsTUFBTSxPQUFPLFNBQVM7SUFtQnBCLFlBQVksT0FBdUIsRUFBRSxrQkFBc0Q7UUFqQnBGLFVBQUssR0FBRyxJQUFJLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBaUIsQ0FBQztRQUd6RCx1QkFBa0IsR0FBRyxPQUFPLENBQUMs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
@@ -0,0 +1,16 @@
1
+ import type { StatePart } from './smartstate.classes.statepart.js';
2
+ export interface IContextProviderOptions<TPayload> {
3
+ /** the context key (compared by strict equality) */
4
+ context: unknown;
5
+ /** the state part to provide */
6
+ statePart: StatePart<any, TPayload>;
7
+ /** optional selector to provide a derived value instead of the full state */
8
+ selectorFn?: (state: TPayload) => any;
9
+ }
10
+ /**
11
+ * attaches a Context Protocol provider to an HTML element.
12
+ * listens for `context-request` events and responds with the state part's value.
13
+ * if subscribe=true, retains the callback and invokes it on every state change.
14
+ * returns a cleanup function that removes the listener and unsubscribes.
15
+ */
16
+ export declare function attachContextProvider<TPayload>(element: HTMLElement, options: IContextProviderOptions<TPayload>): () => void;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * attaches a Context Protocol provider to an HTML element.
3
+ * listens for `context-request` events and responds with the state part's value.
4
+ * if subscribe=true, retains the callback and invokes it on every state change.
5
+ * returns a cleanup function that removes the listener and unsubscribes.
6
+ */
7
+ export function attachContextProvider(element, options) {
8
+ const { context, statePart, selectorFn } = options;
9
+ const subscribers = new Set();
10
+ const subscription = statePart.select(selectorFn).subscribe((value) => {
11
+ for (const cb of subscribers) {
12
+ cb(value);
13
+ }
14
+ });
15
+ const getValue = () => {
16
+ const state = statePart.getState();
17
+ if (state === undefined)
18
+ return undefined;
19
+ return selectorFn ? selectorFn(state) : state;
20
+ };
21
+ const handler = (event) => {
22
+ const e = event;
23
+ const detail = e.detail;
24
+ if (!detail || detail.context !== context)
25
+ return;
26
+ e.stopPropagation();
27
+ if (detail.subscribe) {
28
+ const cb = detail.callback;
29
+ subscribers.add(cb);
30
+ const unsubscribe = () => subscribers.delete(cb);
31
+ cb(getValue(), unsubscribe);
32
+ }
33
+ else {
34
+ detail.callback(getValue());
35
+ }
36
+ };
37
+ element.addEventListener('context-request', handler);
38
+ return () => {
39
+ element.removeEventListener('context-request', handler);
40
+ subscription.unsubscribe();
41
+ subscribers.clear();
42
+ };
43
+ }
44
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRzdGF0ZS5jb250ZXh0cHJvdmlkZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydHN0YXRlLmNvbnRleHRwcm92aWRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFXQTs7Ozs7R0FLRztBQUNILE1BQU0sVUFBVSxxQkFBcUIsQ0FDbkMsT0FBb0IsRUFDcEIsT0FBMEM7SUFFMUMsTUFBTSxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsVUFBVSxFQUFFLEdBQUcsT0FBTyxDQUFDO0lBQ25ELE1BQU0sV0FBVyxHQUFHLElBQUksR0FBRyxFQUFrRCxDQUFDO0lBRTlFLE1BQU0sWUFBWSxHQUFHLFNBQVMsQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsS0FBSyxFQUFFLEVBQUU7UUFDcEUsS0FBSyxNQUFNLEVBQUUsSUFBSSxXQUFXLEVBQUUsQ0FBQztZQUM3QixFQUFFLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDWixDQUFDO0lBQ0gsQ0FBQyxDQUFDLENBQUM7SUFFSCxNQUFNLFFBQVEsR0FBRyxHQUFRLEVBQUU7UUFDekIsTUFBTSxLQUFLLEdBQUcsU0FBUyxDQUFDLFFBQVEsRUFBRSxDQUFDO1FBQ25DLElBQUksS0FBSyxLQUFLLFNBQVM7WUFBRSxPQUFPLFNBQVMsQ0FBQztRQUMxQyxPQUFPLFVBQVUsQ0FBQyxDQUFDLENBQUMsVUFBVSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUM7SUFDaEQsQ0FBQyxDQUFDO0lBRUYsTUFBTSxPQUFPLEdBQUcsQ0FBQyxLQUFZLEVBQUUsRUFBRTtRQUMvQixNQUFNLENBQUMsR0FBRyxLQUFvQixDQUFDO1FBQy9CLE1BQU0sTUFBTSxHQUFHLENBQUMsQ0FBQyxNQUFNLENBQUM7UUFDeEIsSUFBSSxDQUFDLE1BQU0sSUFBSSxNQUFNLENBQUMsT0FBTyxLQUFLLE9BQU87WUFBRSxPQUFPO1FBRWxELENBQUMsQ0FBQyxlQUFlLEVBQUUsQ0FBQztRQUVwQixJQUFJLE1BQU0sQ0FBQyxTQUFTLEVBQUUsQ0FBQztZQUNyQixNQUFNLEVBQUUsR0FBRyxNQUFNLENBQUMsUUFBUSxDQUFDO1lBQzNCLFdBQVcsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUM7WUFDcEIsTUFBTSxXQUFXLEdBQUcsR0FBRyxFQUFFLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsQ0FBQztZQUNqRCxFQUFFLENBQUMsUUFBUSxFQUFFLEVBQUUsV0FBVyxDQUFDLENBQUM7UUFDOUIsQ0FBQzthQUFNLENBQUM7WUFDTixNQUFNLENBQUMsUUFBUSxDQUFDLFFBQVEsRUFBRSxDQUFDLENBQUM7UUFDOUIsQ0FBQztJQUNILENBQUMsQ0FBQztJQUVGLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQyxpQkFBaUIsRUFBRSxPQUFPLENBQUMsQ0FBQztJQUVyRCxPQUFPLEdBQUcsRUFBRTtRQUNWLE9BQU8sQ0FBQyxtQkFBbUIsQ0FBQyxpQkFBaUIsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUN4RCxZQUFZLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDM0IsV0FBVyxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQ3RCLENBQUMsQ0FBQztBQUNKLENBQUMifQ==
package/npmextra.json CHANGED
@@ -16,7 +16,7 @@
16
16
  "githost": "code.foss.global",
17
17
  "gitscope": "push.rocks",
18
18
  "gitrepo": "smartstate",
19
- "description": "A package for handling and managing state in applications.",
19
+ "description": "A TypeScript-first reactive state management library with middleware, computed state, batching, persistence, and Web Component Context Protocol support.",
20
20
  "npmPackagename": "@push.rocks/smartstate",
21
21
  "license": "MIT",
22
22
  "keywords": [
@@ -29,7 +29,13 @@
29
29
  "state selection",
30
30
  "state notification",
31
31
  "asynchronous state",
32
- "cumulative notification"
32
+ "cumulative notification",
33
+ "middleware",
34
+ "computed state",
35
+ "batch updates",
36
+ "context protocol",
37
+ "web components",
38
+ "AbortSignal"
33
39
  ]
34
40
  },
35
41
  "release": {
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@push.rocks/smartstate",
3
- "version": "2.0.31",
3
+ "version": "2.1.1",
4
4
  "private": false,
5
- "description": "A package for handling and managing state in applications.",
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",
7
7
  "typings": "dist_ts/index.d.ts",
8
8
  "type": "module",
@@ -22,7 +22,6 @@
22
22
  "@types/node": "^25.3.2"
23
23
  },
24
24
  "dependencies": {
25
- "@push.rocks/lik": "^6.2.2",
26
25
  "@push.rocks/smarthash": "^3.2.6",
27
26
  "@push.rocks/smartjson": "^6.0.0",
28
27
  "@push.rocks/smartpromise": "^4.2.3",
@@ -54,7 +53,13 @@
54
53
  "state selection",
55
54
  "state notification",
56
55
  "asynchronous state",
57
- "cumulative notification"
56
+ "cumulative notification",
57
+ "middleware",
58
+ "computed state",
59
+ "batch updates",
60
+ "context protocol",
61
+ "web components",
62
+ "AbortSignal"
58
63
  ],
59
64
  "homepage": "https://code.foss.global/push.rocks/smartstate",
60
65
  "repository": {
package/readme.hints.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Smartstate Implementation Notes
2
2
 
3
- ## Current API (as of v2.0.28+)
3
+ ## Current API (as of v2.0.31)
4
4
 
5
5
  ### State Part Initialization
6
6
  - State parts can be created with different init modes: 'soft' (default), 'mandatory', 'force', 'persistent'
@@ -8,53 +8,70 @@
8
8
  - 'mandatory' - requires state part to not exist, fails if it does
9
9
  - 'force' - always creates new state part, overwriting any existing
10
10
  - 'persistent' - like 'soft' but with WebStore persistence (IndexedDB)
11
- - Persistent mode automatically calls init() internally - no need to call it manually
11
+ - Persistent mode automatically calls init() internally
12
12
  - State merge order fixed: initial state takes precedence over stored state
13
13
 
14
14
  ### Actions
15
15
  - Actions are created with `createAction()` method
16
- - Two ways to dispatch actions:
17
- 1. `stateAction.trigger(payload)` - returns Promise<TStatePayload>
18
- 2. `await statePart.dispatchAction(stateAction, payload)` - returns Promise<TStatePayload>
19
- - Both methods return the same Promise, providing flexibility in usage
16
+ - Two ways to dispatch: `stateAction.trigger(payload)` or `statePart.dispatchAction(stateAction, payload)`
17
+ - Both return Promise<TStatePayload>
20
18
 
21
19
  ### State Management Methods
22
- - `select()` - returns Observable with startWith current state, filters undefined states
23
- - `waitUntilPresent()` - waits for specific state condition
20
+ - `select(fn?, { signal? })` - returns Observable, memoized by selector fn ref, supports AbortSignal
21
+ - `waitUntilPresent(fn?, number | { timeoutMs?, signal? })` - waits for state condition, backward compat with number arg
24
22
  - `stateSetup()` - async state initialization with cumulative defer
25
23
  - `notifyChangeCumulative()` - defers notification to end of call stack
26
24
  - `getState()` - returns current state or undefined
27
- - `setState()` - validates state before setting, notifies only on actual changes
25
+ - `setState()` - runs middleware, validates, persists, notifies
26
+ - `addMiddleware(fn)` - intercepts setState, returns removal function
27
+
28
+ ### Middleware
29
+ - Type: `(newState, oldState) => newState | Promise<newState>`
30
+ - Runs sequentially in insertion order before validation/persistence
31
+ - Throw to reject state changes (atomic — state unchanged on error)
32
+ - Does NOT run during initial createStatePart() hydration
33
+
34
+ ### Selector Memoization
35
+ - Uses WeakMap<Function, Observable> for fn-keyed cache
36
+ - `defaultSelectObservable` for no-arg select()
37
+ - Wrapped in `shareReplay({ bufferSize: 1, refCount: true })`
38
+ - NOT cached when AbortSignal is provided
39
+
40
+ ### Batch Updates
41
+ - `smartstate.batch(async () => {...})` — defers notifications until batch completes
42
+ - Supports nesting — only flushes at outermost level
43
+ - StatePart has `smartstateRef` set by `createStatePart()` for batch awareness
44
+ - State parts created via `new StatePart()` directly work without batching
45
+
46
+ ### Computed State
47
+ - `computed(sources, fn)` — standalone function using `combineLatest` + `map`
48
+ - Also available as `smartstate.computed(sources, fn)`
49
+ - Lazy — only subscribes when subscribed to
50
+
51
+ ### Context Protocol Bridge
52
+ - `attachContextProvider(element, { context, statePart, selectorFn? })` — returns cleanup fn
53
+ - Listens for `context-request` CustomEvent on element
54
+ - Supports one-shot and subscription modes
55
+ - Works with Lit @consume(), FAST, or any Context Protocol consumer
28
56
 
29
57
  ### State Hash Detection
30
58
  - Uses SHA256 hash to detect actual state changes
31
- - Fixed: Hash comparison now properly awaits async hash calculation
59
+ - Hash comparison properly awaits async hash calculation
32
60
  - Prevents duplicate notifications for identical state values
33
- - `notifyChange()` is now async to support proper hash comparison
34
61
 
35
62
  ### State Validation
36
63
  - Basic validation ensures state is not null/undefined
37
- - `validateState()` method can be overridden in subclasses for custom validation
38
- - Validation runs on both setState() and when loading from persistent storage
39
-
40
- ### Type System
41
- - Can use either enums or string literal types for state part names
42
- - Test uses simple string types: `type TMyStateParts = 'testStatePart'`
43
- - State can be undefined initially, handled properly in select() and other methods
44
-
45
- ## Recent Fixes (v2.0.24+)
46
- 1. Fixed state hash bug - now properly compares hash values instead of promises
47
- 2. Fixed state initialization merge order - initial state now takes precedence
48
- 3. Ensured stateStore is properly typed as potentially undefined
49
- 4. Simplified init mode logic with clear behavior for each mode
50
- 5. Added state validation with extensible validateState() method
51
- 6. Made notifyChange() async to support proper hash comparison
52
- 7. Updated select() to filter undefined states
53
-
54
- ## Dependency Versions (v2.0.30)
64
+ - `validateState()` can be overridden in subclasses
65
+
66
+ ### Key Notes
67
+ - `smartstateRef` creates circular ref between StatePart and Smartstate
68
+ - Use `===` not deep equality for StatePart comparison in tests
69
+ - Direct rxjs imports used for: Observable, shareReplay, takeUntil, combineLatest, map
70
+
71
+ ## Dependency Versions (v2.0.31)
55
72
  - @git.zone/tsbuild: ^4.1.2
56
73
  - @git.zone/tsbundle: ^2.9.0
57
74
  - @git.zone/tsrun: ^2.0.1
58
75
  - @git.zone/tstest: ^3.1.8
59
76
  - @push.rocks/smartjson: ^6.0.0
60
- - @types/node: ^25.3.2
77
+ - @types/node: ^25.3.2