@veams/status-quo 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +44 -0
- package/README.md +187 -16
- package/dist/hooks/__tests__/state-selector.spec.d.ts +4 -0
- package/dist/hooks/__tests__/state-selector.spec.js +384 -0
- package/dist/hooks/__tests__/state-selector.spec.js.map +1 -0
- package/dist/hooks/__tests__/state-singleton.spec.d.ts +4 -0
- package/dist/hooks/__tests__/state-singleton.spec.js +97 -0
- package/dist/hooks/__tests__/state-singleton.spec.js.map +1 -0
- package/dist/hooks/index.d.ts +3 -0
- package/dist/hooks/index.js +3 -0
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/state-actions.d.ts +2 -0
- package/dist/hooks/state-actions.js +5 -0
- package/dist/hooks/state-actions.js.map +1 -0
- package/dist/hooks/state-factory.d.ts +5 -0
- package/dist/hooks/state-factory.js +10 -6
- package/dist/hooks/state-factory.js.map +1 -1
- package/dist/hooks/state-handler.d.ts +2 -0
- package/dist/hooks/state-handler.js +9 -0
- package/dist/hooks/state-handler.js.map +1 -0
- package/dist/hooks/state-singleton.d.ts +4 -0
- package/dist/hooks/state-singleton.js +3 -5
- package/dist/hooks/state-singleton.js.map +1 -1
- package/dist/hooks/state-subscription-selector.d.ts +5 -0
- package/dist/hooks/state-subscription-selector.js +29 -0
- package/dist/hooks/state-subscription-selector.js.map +1 -0
- package/dist/hooks/state-subscription.d.ts +8 -1
- package/dist/hooks/state-subscription.js +49 -10
- package/dist/hooks/state-subscription.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/store/__tests__/observable-state-handler.spec.js +4 -0
- package/dist/store/__tests__/observable-state-handler.spec.js.map +1 -1
- package/dist/store/index.d.ts +1 -1
- package/dist/store/observable-state-handler.d.ts +2 -0
- package/dist/store/observable-state-handler.js +5 -1
- package/dist/store/observable-state-handler.js.map +1 -1
- package/dist/store/state-singleton.d.ts +4 -1
- package/dist/store/state-singleton.js +11 -2
- package/dist/store/state-singleton.js.map +1 -1
- package/docs/assets/index-Ci4A1zSh.js +187 -0
- package/docs/assets/index-WFTLEHd3.css +1 -0
- package/docs/assets/statusquo-logo-8GVRbxpc.png +0 -0
- package/docs/index.html +13 -0
- package/package.json +1 -1
- package/playground/src/App.tsx +249 -48
- package/playground/src/styles.css +127 -0
- package/src/hooks/__tests__/state-selector.spec.tsx +607 -0
- package/src/hooks/__tests__/state-singleton.spec.tsx +151 -0
- package/src/hooks/index.ts +3 -0
- package/src/hooks/state-actions.tsx +7 -0
- package/src/hooks/state-factory.tsx +32 -6
- package/src/hooks/state-handler.tsx +16 -0
- package/src/hooks/state-singleton.tsx +17 -7
- package/src/hooks/state-subscription-selector.tsx +70 -0
- package/src/hooks/state-subscription.tsx +98 -21
- package/src/index.ts +12 -3
- package/src/store/__tests__/observable-state-handler.spec.ts +6 -0
- package/src/store/index.ts +1 -1
- package/src/store/observable-state-handler.ts +6 -1
- package/src/store/state-singleton.ts +21 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,49 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
6
6
|
|
|
7
|
+
## [1.2.0] - 2026-02-25
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- New composable hook APIs:
|
|
11
|
+
- `useStateHandler(factory, params?)`
|
|
12
|
+
- `useStateActions(handler)`
|
|
13
|
+
- `useStateSubscription(source, selector?, isEqual?)`
|
|
14
|
+
- Selector + equality support across shortcut hooks:
|
|
15
|
+
- `useStateFactory(factory, selector?, isEqual?, params?)`
|
|
16
|
+
- `useStateSingleton(singleton, selector?, isEqual?)`
|
|
17
|
+
- `StateSingletonOptions` with `destroyOnNoConsumers?: boolean` (default: `true`).
|
|
18
|
+
- Ref-counted singleton lifecycle handling so shared singleton instances are only destroyed when the last consumer unmounts.
|
|
19
|
+
- New hook test coverage for:
|
|
20
|
+
- composed API usage (`useStateHandler + useStateActions + useStateSubscription`)
|
|
21
|
+
- selector subscriptions with and without custom equality
|
|
22
|
+
- singleton subscription behavior and `destroyOnNoConsumers: false`
|
|
23
|
+
- full-snapshot subscription behavior.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
- `useStateFactory` now composes `useStateHandler` + `useStateSubscription` internally.
|
|
27
|
+
- `useStateSubscription` now:
|
|
28
|
+
- accepts either `StateSubscriptionHandler` or `StateSingleton`
|
|
29
|
+
- returns `[selectedState, actions]`
|
|
30
|
+
- supports selector/equality without requiring separate selector-specific hook APIs.
|
|
31
|
+
- `useStateSingleton` is now a shortcut over `useStateSubscription(singleton, selector?, isEqual?)`.
|
|
32
|
+
- `makeStateSingleton` now accepts options and manages instance destruction through explicit lifecycle controls.
|
|
33
|
+
- Public exports extended:
|
|
34
|
+
- Added `useStateHandler`, `useStateActions`, `useStateSubscription`
|
|
35
|
+
- Added exported `StateSingletonOptions` type.
|
|
36
|
+
- Observable handler naming aligned with signal convention:
|
|
37
|
+
- Added `getObservable(key)` as the canonical API.
|
|
38
|
+
|
|
39
|
+
### Deprecated
|
|
40
|
+
- `ObservableStateHandler#getObservableItem(key)` is now deprecated in favor of `getObservable(key)`.
|
|
41
|
+
|
|
42
|
+
### Documentation
|
|
43
|
+
- README rewritten with a dedicated API guide that documents base composition, shortcut composition, singleton lifecycle options, and usage examples.
|
|
44
|
+
- Playground documentation significantly expanded:
|
|
45
|
+
- dedicated API section grouped by base composition, shortcut composition, and helper functions
|
|
46
|
+
- clearer singleton lifecycle explanation and examples
|
|
47
|
+
- improved card hierarchy and spacing for readability
|
|
48
|
+
- responsive/toggleable navigation for better mobile usage.
|
|
49
|
+
|
|
7
50
|
## [1.0.0] - 2026-02-17
|
|
8
51
|
|
|
9
52
|
### Added
|
|
@@ -27,4 +70,5 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
27
70
|
- From: `super({ initialState, devTools: { ... } })`
|
|
28
71
|
- To: `super({ initialState, options: { devTools: { ... } } })`
|
|
29
72
|
|
|
73
|
+
[1.2.0]: https://github.com/Veams/status-quo/compare/v1.0.0...v1.2.0
|
|
30
74
|
[1.0.0]: https://github.com/Veams/status-quo/releases/tag/v1.0.0
|
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
# @veams/status-quo
|
|
2
|
-
[](https://www.npmjs.com/package/@veams/status-quo)
|
|
3
|
-
|
|
4
1
|
<center>
|
|
5
2
|
<img src="assets/statusquo-logo.png" width="200" alt="StatusQuo Logo" style="margin: 0 auto;">
|
|
6
3
|
</center>
|
|
7
4
|
|
|
5
|
+
# @veams/status-quo
|
|
6
|
+
[](https://www.npmjs.com/package/@veams/status-quo)
|
|
7
|
+
|
|
8
8
|
The manager to rule your state.
|
|
9
9
|
|
|
10
10
|
This page mirrors the demo content and adds a full API reference.
|
|
@@ -19,10 +19,11 @@ This page mirrors the demo content and adds a full API reference.
|
|
|
19
19
|
6. [Hooks](#hooks)
|
|
20
20
|
7. [Singletons](#singletons)
|
|
21
21
|
8. [Composition](#composition)
|
|
22
|
-
9. [
|
|
23
|
-
10. [
|
|
24
|
-
11. [
|
|
25
|
-
12. [
|
|
22
|
+
9. [API Guide](#api-guide)
|
|
23
|
+
10. [Devtools](#devtools)
|
|
24
|
+
11. [Cleanup](#cleanup)
|
|
25
|
+
12. [API Reference](#api-reference)
|
|
26
|
+
13. [Migration](#migration)
|
|
26
27
|
|
|
27
28
|
## Overview
|
|
28
29
|
|
|
@@ -87,22 +88,54 @@ Both are built on `BaseStateHandler`, which provides the shared lifecycle and de
|
|
|
87
88
|
|
|
88
89
|
## Hooks
|
|
89
90
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
Use `useStateHandler + useStateActions + useStateSubscription` as the base composition.
|
|
92
|
+
`useStateFactory` and `useStateSingleton` are shortcut APIs over that composition.
|
|
93
|
+
For full signatures and practical examples, see [API Guide](#api-guide).
|
|
94
|
+
|
|
95
|
+
- `useStateHandler(factory, params)`
|
|
96
|
+
- Creates and memoizes one handler instance per component.
|
|
97
|
+
- `useStateActions(handler)`
|
|
98
|
+
- Returns actions without subscribing to state.
|
|
99
|
+
- `useStateSubscription(handlerOrSingleton, selector?, isEqual?)`
|
|
100
|
+
- Subscribes to full state or a selected slice and returns `[state, actions]`.
|
|
101
|
+
- `useStateFactory(factory, selector?, isEqual?, params?)`
|
|
102
|
+
- Shortcut for `useStateHandler + useStateSubscription`.
|
|
103
|
+
- `useStateSingleton(singleton, selector?, isEqual?)`
|
|
104
|
+
- Shortcut for `useStateSubscription(singleton, selector?, isEqual?)`.
|
|
105
|
+
|
|
106
|
+
Recommended composition:
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
const handler = useStateHandler(createUserStore, []);
|
|
110
|
+
const actions = useStateActions(handler);
|
|
111
|
+
const [name] = useStateSubscription(handler, (state) => state.user.name);
|
|
112
|
+
|
|
113
|
+
const [singletonName] = useStateSubscription(UserSingleton, (state) => state.user.name);
|
|
114
|
+
```
|
|
95
115
|
|
|
96
116
|
## Singletons
|
|
97
117
|
|
|
118
|
+
Use singletons for shared state across multiple components.
|
|
119
|
+
|
|
98
120
|
```ts
|
|
99
121
|
import { makeStateSingleton, useStateSingleton } from '@veams/status-quo';
|
|
100
122
|
|
|
123
|
+
// Default behavior: singleton is destroyed when the last consumer unmounts.
|
|
101
124
|
const CounterSingleton = makeStateSingleton(() => new CounterStore());
|
|
102
125
|
|
|
103
126
|
const [state, actions] = useStateSingleton(CounterSingleton);
|
|
104
127
|
```
|
|
105
128
|
|
|
129
|
+
Keep a singleton instance alive across unmounts:
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
const PersistentCounterSingleton = makeStateSingleton(() => new CounterStore(), {
|
|
133
|
+
destroyOnNoConsumers: false,
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Use this for app-level stores that should survive route/component unmounts. Keep the default for stores that should release resources when unused.
|
|
138
|
+
|
|
106
139
|
## Composition
|
|
107
140
|
|
|
108
141
|
Use only the slice you need. RxJS makes multi-source composition powerful and declarative with operators like `combineLatest`, `switchMap`, or `debounceTime`. Signals can derive values with `computed` and wire them into a parent store via `bindSubscribable`.
|
|
@@ -155,6 +188,129 @@ class AppSignalStore extends SignalStateHandler<AppState, AppActions> {
|
|
|
155
188
|
}
|
|
156
189
|
```
|
|
157
190
|
|
|
191
|
+
## API Guide
|
|
192
|
+
|
|
193
|
+
This section documents the primary public API with behavior notes and usage examples.
|
|
194
|
+
|
|
195
|
+
### `useStateHandler(factory, params?)`
|
|
196
|
+
|
|
197
|
+
Creates one handler instance per component mount and returns it.
|
|
198
|
+
|
|
199
|
+
- `factory`: function returning a `StateSubscriptionHandler`
|
|
200
|
+
- `params`: optional factory params tuple
|
|
201
|
+
- lifecycle note: params are applied when the handler instance is created for that mount
|
|
202
|
+
|
|
203
|
+
```ts
|
|
204
|
+
const handler = useStateHandler(createUserStore, []);
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### `useStateActions(handler)`
|
|
208
|
+
|
|
209
|
+
Returns actions from a handler without subscribing to state changes.
|
|
210
|
+
Use this in action-only components to avoid rerenders from state updates.
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
const handler = useStateHandler(createUserStore, []);
|
|
214
|
+
const actions = useStateActions(handler);
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### `useStateSubscription(source, selector?, isEqual?)`
|
|
218
|
+
|
|
219
|
+
Subscribes to either a handler instance or a singleton and returns `[selectedState, actions]`.
|
|
220
|
+
|
|
221
|
+
- `source`: `StateSubscriptionHandler` or `StateSingleton`
|
|
222
|
+
- `selector`: optional projection function; defaults to identity
|
|
223
|
+
- `isEqual`: optional equality function; defaults to `Object.is`
|
|
224
|
+
|
|
225
|
+
Full snapshot subscription:
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
const handler = useStateHandler(createUserStore, []);
|
|
229
|
+
const [state, actions] = useStateSubscription(handler);
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Selector subscription:
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
const [name, actions] = useStateSubscription(
|
|
236
|
+
handler,
|
|
237
|
+
(state) => state.user.name
|
|
238
|
+
);
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Selector with custom equality:
|
|
242
|
+
|
|
243
|
+
```ts
|
|
244
|
+
const [profile] = useStateSubscription(
|
|
245
|
+
handler,
|
|
246
|
+
(state) => state.user.profile,
|
|
247
|
+
(current, next) => current.id === next.id && current.role === next.role
|
|
248
|
+
);
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Singleton source:
|
|
252
|
+
|
|
253
|
+
```ts
|
|
254
|
+
const [session, actions] = useStateSubscription(SessionSingleton);
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Lifecycle note for singleton sources:
|
|
258
|
+
- Consumers are ref-counted.
|
|
259
|
+
- The singleton instance is only destroyed when the last consumer unmounts and `destroyOnNoConsumers !== false`.
|
|
260
|
+
|
|
261
|
+
### `useStateFactory(factory, selector?, isEqual?, params?)`
|
|
262
|
+
|
|
263
|
+
Shortcut API for `useStateHandler + useStateSubscription`.
|
|
264
|
+
|
|
265
|
+
- `useStateFactory(factory, params)`
|
|
266
|
+
- `useStateFactory(factory, selector, params)`
|
|
267
|
+
- `useStateFactory(factory, selector, isEqual, params)`
|
|
268
|
+
|
|
269
|
+
```ts
|
|
270
|
+
const [state, actions] = useStateFactory(createUserStore, []);
|
|
271
|
+
const [name] = useStateFactory(createUserStore, (state) => state.user.name, []);
|
|
272
|
+
const [profile] = useStateFactory(
|
|
273
|
+
createUserStore,
|
|
274
|
+
(state) => state.user.profile,
|
|
275
|
+
(current, next) => current.id === next.id,
|
|
276
|
+
[]
|
|
277
|
+
);
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### `makeStateSingleton(factory, options?)`
|
|
281
|
+
|
|
282
|
+
Creates a shared singleton provider for a handler instance.
|
|
283
|
+
|
|
284
|
+
```ts
|
|
285
|
+
const UserSingleton = makeStateSingleton(() => new UserStore());
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Options:
|
|
289
|
+
|
|
290
|
+
```ts
|
|
291
|
+
type StateSingletonOptions = {
|
|
292
|
+
destroyOnNoConsumers?: boolean; // default: true
|
|
293
|
+
};
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
- `true` (default): destroy instance after last consumer unmounts
|
|
297
|
+
- `false`: keep instance alive across periods with zero consumers
|
|
298
|
+
|
|
299
|
+
```ts
|
|
300
|
+
const PersistentUserSingleton = makeStateSingleton(() => new UserStore(), {
|
|
301
|
+
destroyOnNoConsumers: false,
|
|
302
|
+
});
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### `useStateSingleton(singleton, selector?, isEqual?)`
|
|
306
|
+
|
|
307
|
+
Shortcut API for `useStateSubscription(singleton, selector?, isEqual?)`.
|
|
308
|
+
|
|
309
|
+
```ts
|
|
310
|
+
const [state, actions] = useStateSingleton(UserSingleton);
|
|
311
|
+
const [name] = useStateSingleton(UserSingleton, (state) => state.user.name);
|
|
312
|
+
```
|
|
313
|
+
|
|
158
314
|
## Devtools
|
|
159
315
|
|
|
160
316
|
Enable Redux Devtools integration with `options.devTools`:
|
|
@@ -249,7 +405,7 @@ Public methods:
|
|
|
249
405
|
|
|
250
406
|
- `getStateAsObservable(options?: { useDistinctUntilChanged?: boolean }): Observable<S>`
|
|
251
407
|
- `getStateItemAsObservable(key: keyof S): Observable<S[keyof S]>`
|
|
252
|
-
- `
|
|
408
|
+
- `getObservable(key: keyof S): Observable<S[keyof S]>`
|
|
253
409
|
- `subscribe(listener: () => void): () => void`
|
|
254
410
|
|
|
255
411
|
Notes:
|
|
@@ -286,18 +442,33 @@ Notes:
|
|
|
286
442
|
### `makeStateSingleton`
|
|
287
443
|
|
|
288
444
|
```ts
|
|
445
|
+
type StateSingletonOptions = {
|
|
446
|
+
destroyOnNoConsumers?: boolean; // default: true
|
|
447
|
+
};
|
|
448
|
+
|
|
289
449
|
function makeStateSingleton<S, A>(
|
|
290
|
-
factory: () => StateSubscriptionHandler<S, A
|
|
450
|
+
factory: () => StateSubscriptionHandler<S, A>,
|
|
451
|
+
options?: StateSingletonOptions
|
|
291
452
|
): {
|
|
292
453
|
getInstance: () => StateSubscriptionHandler<S, A>;
|
|
293
454
|
}
|
|
294
455
|
```
|
|
295
456
|
|
|
457
|
+
Lifecycle behavior:
|
|
458
|
+
- `destroyOnNoConsumers: true` (default): destroy and recreate singleton instances with mount lifecycle.
|
|
459
|
+
- `destroyOnNoConsumers: false`: keep the same singleton instance alive when no component is subscribed.
|
|
460
|
+
|
|
296
461
|
### Hooks
|
|
297
462
|
|
|
298
|
-
- `
|
|
463
|
+
- `useStateHandler<V, A, P extends unknown[]>(factory: (...args: P) => StateSubscriptionHandler<V, A>, params?: P)`
|
|
464
|
+
- Returns `StateSubscriptionHandler<V, A>`.
|
|
465
|
+
- `useStateActions<V, A>(handler: StateSubscriptionHandler<V, A>)`
|
|
466
|
+
- Returns `A`.
|
|
467
|
+
- `useStateSubscription<V, A, Sel = V>(source: StateSubscriptionHandler<V, A> | StateSingleton<V, A>, selector?: (state: V) => Sel, isEqual?: (current: Sel, next: Sel) => boolean)`
|
|
468
|
+
- Returns `[state, actions]`.
|
|
469
|
+
- `useStateFactory<V, A, P extends unknown[], Sel = V>(factory: (...args: P) => StateSubscriptionHandler<V, A>, selector?: (state: V) => Sel, isEqual?: (current: Sel, next: Sel) => boolean, params?: P)`
|
|
299
470
|
- Returns `[state, actions]`.
|
|
300
|
-
- `useStateSingleton<V, A>(singleton: StateSingleton<V, A
|
|
471
|
+
- `useStateSingleton<V, A, Sel = V>(singleton: StateSingleton<V, A>, selector?: (state: V) => Sel, isEqual?: (current: Sel, next: Sel) => boolean)`
|
|
301
472
|
- Returns `[state, actions]`.
|
|
302
473
|
|
|
303
474
|
## Migration
|