@veams/status-quo 1.5.1 → 1.7.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/.turbo/turbo-build.log +12 -0
- package/.turbo/turbo-check$colon$types.log +4 -0
- package/.turbo/turbo-docs$colon$build.log +14 -0
- package/.turbo/turbo-lint.log +8 -0
- package/.turbo/turbo-test.log +15 -0
- package/CHANGELOG.md +24 -3
- package/README.md +217 -41
- package/dist/config/status-quo-config.d.ts +13 -0
- package/dist/config/status-quo-config.js +14 -0
- package/dist/config/status-quo-config.js.map +1 -1
- package/dist/hooks/__tests__/state-provider.spec.d.ts +4 -0
- package/dist/hooks/__tests__/state-provider.spec.js +179 -0
- package/dist/hooks/__tests__/state-provider.spec.js.map +1 -0
- package/dist/hooks/__tests__/state-selector.spec.js +11 -12
- package/dist/hooks/__tests__/state-selector.spec.js.map +1 -1
- package/dist/hooks/__tests__/state-singleton.spec.js +10 -11
- package/dist/hooks/__tests__/state-singleton.spec.js.map +1 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/state-factory.js.map +1 -1
- package/dist/hooks/state-provider.d.ts +14 -0
- package/dist/hooks/state-provider.js +24 -0
- package/dist/hooks/state-provider.js.map +1 -0
- package/dist/hooks/state-subscription-selector.js +6 -2
- package/dist/hooks/state-subscription-selector.js.map +1 -1
- package/dist/hooks/state-subscription.js +1 -1
- package/dist/hooks/state-subscription.js.map +1 -1
- package/dist/index.d.ts +4 -5
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/react/hooks/__tests__/state-provider.spec.d.ts +4 -0
- package/dist/react/hooks/__tests__/state-provider.spec.js +179 -0
- package/dist/react/hooks/__tests__/state-provider.spec.js.map +1 -0
- package/dist/react/hooks/__tests__/state-selector.spec.d.ts +4 -0
- package/dist/react/hooks/__tests__/state-selector.spec.js +547 -0
- package/dist/react/hooks/__tests__/state-selector.spec.js.map +1 -0
- package/dist/react/hooks/__tests__/state-singleton.spec.d.ts +4 -0
- package/dist/react/hooks/__tests__/state-singleton.spec.js +96 -0
- package/dist/react/hooks/__tests__/state-singleton.spec.js.map +1 -0
- package/{src/hooks/index.ts → dist/react/hooks/index.d.ts} +1 -0
- package/dist/react/hooks/index.js +7 -0
- package/dist/react/hooks/index.js.map +1 -0
- package/dist/react/hooks/state-actions.d.ts +2 -0
- package/dist/react/hooks/state-actions.js +5 -0
- package/dist/react/hooks/state-actions.js.map +1 -0
- package/dist/react/hooks/state-factory.d.ts +7 -0
- package/dist/react/hooks/state-factory.js +13 -0
- package/dist/react/hooks/state-factory.js.map +1 -0
- package/dist/react/hooks/state-handler.d.ts +2 -0
- package/dist/react/hooks/state-handler.js +9 -0
- package/dist/react/hooks/state-handler.js.map +1 -0
- package/dist/react/hooks/state-provider.d.ts +14 -0
- package/dist/react/hooks/state-provider.js +24 -0
- package/dist/react/hooks/state-provider.js.map +1 -0
- package/dist/react/hooks/state-singleton.d.ts +6 -0
- package/dist/react/hooks/state-singleton.js +7 -0
- package/dist/react/hooks/state-singleton.js.map +1 -0
- package/dist/react/hooks/state-subscription-selector.d.ts +3 -0
- package/dist/react/hooks/state-subscription-selector.js +114 -0
- package/dist/react/hooks/state-subscription-selector.js.map +1 -0
- package/dist/react/hooks/state-subscription.d.ts +9 -0
- package/dist/react/hooks/state-subscription.js +53 -0
- package/dist/react/hooks/state-subscription.js.map +1 -0
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.js +2 -0
- package/dist/react/index.js.map +1 -0
- package/dist/store/__tests__/observable-state-handler.spec.js +66 -11
- package/dist/store/__tests__/observable-state-handler.spec.js.map +1 -1
- package/dist/store/__tests__/signal-state-handler.spec.js.map +1 -1
- package/dist/store/base-state-handler.d.ts +3 -5
- package/dist/store/base-state-handler.js +10 -9
- package/dist/store/base-state-handler.js.map +1 -1
- package/dist/store/dev-tools.js +0 -3
- package/dist/store/dev-tools.js.map +1 -1
- package/dist/store/observable-state-handler.d.ts +4 -10
- package/dist/store/observable-state-handler.js +4 -11
- package/dist/store/observable-state-handler.js.map +1 -1
- package/dist/store/signal-state-handler.d.ts +2 -5
- package/dist/store/signal-state-handler.js +3 -2
- package/dist/store/signal-state-handler.js.map +1 -1
- package/dist/store/state-singleton.js +1 -1
- package/dist/store/state-singleton.js.map +1 -1
- package/eslint.config.mjs +75 -0
- package/package.json +18 -18
- package/src/config/status-quo-config.ts +31 -1
- package/src/index.ts +11 -15
- package/src/react/hooks/__tests__/state-provider.spec.tsx +286 -0
- package/src/{hooks → react/hooks}/__tests__/state-selector.spec.tsx +118 -44
- package/src/{hooks → react/hooks}/__tests__/state-singleton.spec.tsx +21 -20
- package/src/react/hooks/index.ts +11 -0
- package/src/{hooks → react/hooks}/state-actions.tsx +1 -1
- package/src/{hooks → react/hooks}/state-factory.tsx +2 -2
- package/src/{hooks → react/hooks}/state-handler.tsx +1 -1
- package/src/react/hooks/state-provider.tsx +56 -0
- package/src/{hooks → react/hooks}/state-singleton.tsx +1 -1
- package/src/react/hooks/state-subscription-selector.tsx +190 -0
- package/src/{hooks → react/hooks}/state-subscription.tsx +5 -9
- package/src/react/index.ts +1 -0
- package/src/store/__tests__/observable-state-handler.spec.ts +92 -13
- package/src/store/__tests__/signal-state-handler.spec.ts +5 -1
- package/src/store/base-state-handler.ts +17 -22
- package/src/store/dev-tools.ts +3 -3
- package/src/store/observable-state-handler.ts +12 -22
- package/src/store/signal-state-handler.ts +11 -8
- package/src/store/state-singleton.ts +1 -1
- package/tsconfig.json +2 -3
- package/.eslintrc.cjs +0 -132
- package/.github/workflows/pages.yml +0 -46
- package/.github/workflows/release.yml +0 -33
- package/.nvmrc +0 -1
- package/.prettierrc +0 -7
- package/docs/assets/index-BBmpszOW.css +0 -1
- package/docs/assets/index-Cf8El_RO.js +0 -194
- package/docs/assets/statusquo-logo-8GVRbxpc.png +0 -0
- package/docs/index.html +0 -13
- package/playground/index.html +0 -12
- package/playground/src/App.tsx +0 -699
- package/playground/src/assets/philosophy-agnostic.svg +0 -18
- package/playground/src/assets/philosophy-separation.svg +0 -13
- package/playground/src/assets/philosophy-swap.svg +0 -17
- package/playground/src/assets/statusquo-logo.png +0 -0
- package/playground/src/main.tsx +0 -19
- package/playground/src/styles.css +0 -534
- package/playground/tsconfig.json +0 -12
- package/playground/vite.config.ts +0 -18
- package/src/hooks/state-subscription-selector.tsx +0 -111
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
|
|
2
|
+
> @veams/status-quo@1.5.1 docs:build
|
|
3
|
+
> vite build --config playground/vite.config.ts
|
|
4
|
+
|
|
5
|
+
vite v5.4.21 building for production...
|
|
6
|
+
transforming...
|
|
7
|
+
✓ 285 modules transformed.
|
|
8
|
+
rendering chunks...
|
|
9
|
+
computing gzip size...
|
|
10
|
+
../docs/index.html 0.41 kB │ gzip: 0.27 kB
|
|
11
|
+
../docs/assets/statusquo-logo-8GVRbxpc.png 87.06 kB
|
|
12
|
+
../docs/assets/index-BBmpszOW.css 8.73 kB │ gzip: 2.60 kB
|
|
13
|
+
../docs/assets/index-BsFqA6fh.js 268.01 kB │ gzip: 83.23 kB
|
|
14
|
+
✓ built in 1.18s
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
> @veams/status-quo@1.5.1 test
|
|
3
|
+
> cross-env NODE_ENV=test jest --config jest.config.cjs
|
|
4
|
+
|
|
5
|
+
PASS src/react/hooks/__tests__/state-selector.spec.tsx
|
|
6
|
+
PASS src/react/hooks/__tests__/state-provider.spec.tsx
|
|
7
|
+
PASS src/react/hooks/__tests__/state-singleton.spec.tsx
|
|
8
|
+
PASS src/store/__tests__/observable-state-handler.spec.ts
|
|
9
|
+
PASS src/store/__tests__/signal-state-handler.spec.ts
|
|
10
|
+
|
|
11
|
+
Test Suites: 5 passed, 5 total
|
|
12
|
+
Tests: 40 passed, 40 total
|
|
13
|
+
Snapshots: 0 total
|
|
14
|
+
Time: 1.703 s
|
|
15
|
+
Ran all test suites.
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,27 @@ 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
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- `StateProvider` for sharing one handler instance across a scoped React subtree.
|
|
11
|
+
- `useProvidedStateHandler()`, `useProvidedStateActions()`, and `useProvidedStateSubscription()` for provider-scoped composition.
|
|
12
|
+
- `setupStatusQuo({ devTools: { enabled } })` for global Redux DevTools defaults.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- React-specific exports now live under `@veams/status-quo/react`.
|
|
16
|
+
- `options.devTools.namespace` is now optional and falls back to the handler class name.
|
|
17
|
+
- `ObservableStateHandler#getObservable(options?)` is now the canonical API for the full state stream.
|
|
18
|
+
- `ObservableStateHandler#getObservableItem(key)` is now the canonical API for keyed item streams.
|
|
19
|
+
- `makeStateSingleton()` now keeps singleton instances alive by default unless `destroyOnNoConsumers: true` is set.
|
|
20
|
+
|
|
21
|
+
### Removed
|
|
22
|
+
- React hook and provider exports from the root `@veams/status-quo` entrypoint in favor of `@veams/status-quo/react`.
|
|
23
|
+
- `@veams/status-quo/hooks` in favor of `@veams/status-quo/react`
|
|
24
|
+
- `ObservableStateHandler#getStateAsObservable(options?)`
|
|
25
|
+
- `ObservableStateHandler#getStateItemAsObservable(key)`
|
|
26
|
+
- keyed `ObservableStateHandler#getObservable(key)` in favor of `getObservableItem(key)`
|
|
27
|
+
|
|
7
28
|
## [1.3.0] - 2026-02-25
|
|
8
29
|
|
|
9
30
|
### Added
|
|
@@ -78,6 +99,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
78
99
|
- From: `super({ initialState, devTools: { ... } })`
|
|
79
100
|
- To: `super({ initialState, options: { devTools: { ... } } })`
|
|
80
101
|
|
|
81
|
-
[1.3.0]: https://github.com/Veams/
|
|
82
|
-
[1.2.0]: https://github.com/Veams/
|
|
83
|
-
[1.0.0]: https://github.com/Veams/
|
|
102
|
+
[1.3.0]: https://github.com/Veams/veams/compare/v1.2.0...v1.3.0
|
|
103
|
+
[1.2.0]: https://github.com/Veams/veams/compare/v1.0.0...v1.2.0
|
|
104
|
+
[1.0.0]: https://github.com/Veams/veams/releases/tag/v1.0.0
|
package/README.md
CHANGED
|
@@ -7,23 +7,24 @@
|
|
|
7
7
|
|
|
8
8
|
The manager to rule your state.
|
|
9
9
|
|
|
10
|
-
This
|
|
10
|
+
This README summarizes the package API. The full routed documentation lives in VEAMS Documentation.
|
|
11
11
|
|
|
12
12
|
## Table of Contents
|
|
13
13
|
|
|
14
14
|
1. [Overview](#overview)
|
|
15
15
|
2. [Philosophy](#philosophy)
|
|
16
|
-
3. [
|
|
16
|
+
3. [Docs](#docs)
|
|
17
17
|
4. [Quickstart](#quickstart)
|
|
18
18
|
5. [Handlers](#handlers)
|
|
19
19
|
6. [Hooks](#hooks)
|
|
20
|
-
7. [
|
|
21
|
-
8. [
|
|
22
|
-
9. [
|
|
23
|
-
10. [
|
|
24
|
-
11. [
|
|
25
|
-
12. [
|
|
26
|
-
13. [
|
|
20
|
+
7. [Providers](#providers)
|
|
21
|
+
8. [Singletons](#singletons)
|
|
22
|
+
9. [Composition](#composition)
|
|
23
|
+
10. [API Guide](#api-guide)
|
|
24
|
+
11. [Devtools](#devtools)
|
|
25
|
+
12. [Cleanup](#cleanup)
|
|
26
|
+
13. [API Reference](#api-reference)
|
|
27
|
+
14. [Migration](#migration)
|
|
27
28
|
|
|
28
29
|
## Overview
|
|
29
30
|
|
|
@@ -35,11 +36,11 @@ StatusQuo is a small, framework-agnostic state layer that focuses on explicit li
|
|
|
35
36
|
- Separate view and state. Handlers own transitions and expose actions; views subscribe to snapshots.
|
|
36
37
|
- Framework-agnostic core. Business logic lives outside the UI library; hooks provide the glue.
|
|
37
38
|
|
|
38
|
-
##
|
|
39
|
+
## Docs
|
|
39
40
|
|
|
40
|
-
Live docs
|
|
41
|
+
Live docs:
|
|
41
42
|
|
|
42
|
-
[https://veams.github.io/status-quo/](https://veams.github.io/status-quo/)
|
|
43
|
+
[https://veams.github.io/status-quo/packages/status-quo/getting-started](https://veams.github.io/status-quo/packages/status-quo/getting-started)
|
|
43
44
|
|
|
44
45
|
## Quickstart
|
|
45
46
|
|
|
@@ -52,7 +53,8 @@ npm install @veams/status-quo rxjs @preact/signals-core
|
|
|
52
53
|
Create a store and use it in a component:
|
|
53
54
|
|
|
54
55
|
```ts
|
|
55
|
-
import { ObservableStateHandler
|
|
56
|
+
import { ObservableStateHandler } from '@veams/status-quo';
|
|
57
|
+
import { useStateFactory } from '@veams/status-quo/react';
|
|
56
58
|
|
|
57
59
|
type CounterState = { count: number };
|
|
58
60
|
|
|
@@ -77,13 +79,16 @@ class CounterStore extends ObservableStateHandler<CounterState, CounterActions>
|
|
|
77
79
|
const [state, actions] = useStateFactory(() => new CounterStore(), []);
|
|
78
80
|
```
|
|
79
81
|
|
|
80
|
-
Optional global setup
|
|
82
|
+
Optional global setup for shared defaults such as distinct behavior and Redux DevTools:
|
|
81
83
|
|
|
82
84
|
```ts
|
|
83
85
|
import equal from 'fast-deep-equal';
|
|
84
86
|
import { setupStatusQuo } from '@veams/status-quo';
|
|
85
87
|
|
|
86
88
|
setupStatusQuo({
|
|
89
|
+
devTools: {
|
|
90
|
+
enabled: true,
|
|
91
|
+
},
|
|
87
92
|
distinct: {
|
|
88
93
|
comparator: equal,
|
|
89
94
|
},
|
|
@@ -101,12 +106,22 @@ Both are built on `BaseStateHandler`, which provides the shared lifecycle and de
|
|
|
101
106
|
|
|
102
107
|
## Hooks
|
|
103
108
|
|
|
109
|
+
The React layer lives under `@veams/status-quo/react`.
|
|
110
|
+
|
|
104
111
|
Use `useStateHandler + useStateActions + useStateSubscription` as the base composition.
|
|
105
112
|
`useStateFactory` and `useStateSingleton` are shortcut APIs over that composition.
|
|
106
113
|
For full signatures and practical examples, see [API Guide](#api-guide).
|
|
107
114
|
|
|
108
115
|
- `useStateHandler(factory, params)`
|
|
109
116
|
- Creates and memoizes one handler instance per component.
|
|
117
|
+
- `StateProvider({ instance })`
|
|
118
|
+
- Shares one handler instance with a subtree through React context.
|
|
119
|
+
- `useProvidedStateHandler()`
|
|
120
|
+
- Reads the nearest provider-scoped handler instance.
|
|
121
|
+
- `useProvidedStateActions()`
|
|
122
|
+
- Returns provider-scoped actions without subscribing to state.
|
|
123
|
+
- `useProvidedStateSubscription(selector?, isEqual?)`
|
|
124
|
+
- Subscribes to provider-scoped state and returns `[state, actions]`.
|
|
110
125
|
- `useStateActions(handler)`
|
|
111
126
|
- Returns actions without subscribing to state.
|
|
112
127
|
- `useStateSubscription(handlerOrSingleton, selector?, isEqual?)`
|
|
@@ -119,6 +134,12 @@ For full signatures and practical examples, see [API Guide](#api-guide).
|
|
|
119
134
|
Recommended composition:
|
|
120
135
|
|
|
121
136
|
```ts
|
|
137
|
+
import {
|
|
138
|
+
useStateActions,
|
|
139
|
+
useStateHandler,
|
|
140
|
+
useStateSubscription,
|
|
141
|
+
} from '@veams/status-quo/react';
|
|
142
|
+
|
|
122
143
|
const handler = useStateHandler(createUserStore, []);
|
|
123
144
|
const actions = useStateActions(handler);
|
|
124
145
|
const [name] = useStateSubscription(handler, (state) => state.user.name);
|
|
@@ -126,28 +147,72 @@ const [name] = useStateSubscription(handler, (state) => state.user.name);
|
|
|
126
147
|
const [singletonName] = useStateSubscription(UserSingleton, (state) => state.user.name);
|
|
127
148
|
```
|
|
128
149
|
|
|
150
|
+
## Providers
|
|
151
|
+
|
|
152
|
+
Use provider scope when a parent should own one local handler instance and several descendants need access to that same instance.
|
|
153
|
+
|
|
154
|
+
```tsx
|
|
155
|
+
import {
|
|
156
|
+
StateProvider,
|
|
157
|
+
useProvidedStateActions,
|
|
158
|
+
useProvidedStateSubscription,
|
|
159
|
+
useStateHandler,
|
|
160
|
+
} from '@veams/status-quo/react';
|
|
161
|
+
|
|
162
|
+
function CounterScope() {
|
|
163
|
+
const handler = useStateHandler(createCounterStore, []);
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<StateProvider instance={handler}>
|
|
167
|
+
<CounterValue />
|
|
168
|
+
<CounterButtons />
|
|
169
|
+
</StateProvider>
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function CounterValue() {
|
|
174
|
+
const [count] = useProvidedStateSubscription((state: CounterState) => state.count);
|
|
175
|
+
|
|
176
|
+
return <strong>{count}</strong>;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function CounterButtons() {
|
|
180
|
+
const actions = useProvidedStateActions<CounterState, CounterActions>();
|
|
181
|
+
|
|
182
|
+
return <button onClick={actions.increase}>Increase</button>;
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
This is the scoped-sharing option between a local handler and a singleton:
|
|
187
|
+
|
|
188
|
+
- parent owns lifecycle once
|
|
189
|
+
- children choose whether they need state, actions, or the raw handler
|
|
190
|
+
- action-only components stay out of rerender fanout
|
|
191
|
+
- no prop drilling just to move a handler through the tree
|
|
192
|
+
|
|
129
193
|
## Singletons
|
|
130
194
|
|
|
131
195
|
Use singletons for shared state across multiple components.
|
|
132
196
|
|
|
133
197
|
```ts
|
|
134
|
-
import { makeStateSingleton
|
|
198
|
+
import { makeStateSingleton } from '@veams/status-quo';
|
|
199
|
+
import { useStateSingleton } from '@veams/status-quo/react';
|
|
135
200
|
|
|
136
|
-
// Default behavior: singleton
|
|
201
|
+
// Default behavior: singleton stays alive across unmounts.
|
|
137
202
|
const CounterSingleton = makeStateSingleton(() => new CounterStore());
|
|
138
203
|
|
|
139
204
|
const [state, actions] = useStateSingleton(CounterSingleton);
|
|
140
205
|
```
|
|
141
206
|
|
|
142
|
-
|
|
207
|
+
Destroy a singleton instance when the last consumer unmounts:
|
|
143
208
|
|
|
144
209
|
```ts
|
|
145
|
-
const
|
|
146
|
-
destroyOnNoConsumers:
|
|
210
|
+
const RouteScopedCounterSingleton = makeStateSingleton(() => new CounterStore(), {
|
|
211
|
+
destroyOnNoConsumers: true,
|
|
147
212
|
});
|
|
148
213
|
```
|
|
149
214
|
|
|
150
|
-
|
|
215
|
+
Keep the default for app-level shared state that should survive route/component unmounts. Use `destroyOnNoConsumers: true` when the shared handler should behave more like a mounted resource.
|
|
151
216
|
|
|
152
217
|
## Composition
|
|
153
218
|
|
|
@@ -158,7 +223,7 @@ import { combineLatest } from 'rxjs';
|
|
|
158
223
|
|
|
159
224
|
// RxJS: combine handler streams (RxJS shines here)
|
|
160
225
|
class AppSignalStore extends SignalStateHandler<AppState, AppActions> {
|
|
161
|
-
private counter$ = CounterObservableStore.getInstance().
|
|
226
|
+
private counter$ = CounterObservableStore.getInstance().getObservable();
|
|
162
227
|
private card$ = new CardObservableHandler();
|
|
163
228
|
|
|
164
229
|
constructor() {
|
|
@@ -207,11 +272,14 @@ This section documents the primary public API with behavior notes and usage exam
|
|
|
207
272
|
|
|
208
273
|
### `setupStatusQuo(config?)`
|
|
209
274
|
|
|
210
|
-
Sets global runtime defaults for distinct update behavior.
|
|
275
|
+
Sets global runtime defaults for distinct update behavior and Redux DevTools enablement.
|
|
211
276
|
Per-handler options still override the global setup.
|
|
212
277
|
|
|
213
278
|
```ts
|
|
214
279
|
type StatusQuoConfig = {
|
|
280
|
+
devTools?: {
|
|
281
|
+
enabled?: boolean; // default: false
|
|
282
|
+
};
|
|
215
283
|
distinct?: {
|
|
216
284
|
enabled?: boolean; // default: true
|
|
217
285
|
comparator?: (previous: unknown, next: unknown) => boolean; // default: JSON compare
|
|
@@ -224,6 +292,9 @@ import equal from 'fast-deep-equal';
|
|
|
224
292
|
import { setupStatusQuo } from '@veams/status-quo';
|
|
225
293
|
|
|
226
294
|
setupStatusQuo({
|
|
295
|
+
devTools: {
|
|
296
|
+
enabled: true,
|
|
297
|
+
},
|
|
227
298
|
distinct: {
|
|
228
299
|
comparator: equal,
|
|
229
300
|
},
|
|
@@ -239,19 +310,58 @@ Creates one handler instance per component mount and returns it.
|
|
|
239
310
|
- lifecycle note: params are applied when the handler instance is created for that mount
|
|
240
311
|
|
|
241
312
|
```ts
|
|
313
|
+
import { useStateHandler } from '@veams/status-quo/react';
|
|
314
|
+
|
|
242
315
|
const handler = useStateHandler(createUserStore, []);
|
|
243
316
|
```
|
|
244
317
|
|
|
318
|
+
### `StateProvider`
|
|
319
|
+
|
|
320
|
+
Shares one existing handler instance with a subtree through React context.
|
|
321
|
+
Use this when a parent owns lifecycle and descendants should consume the same local instance without prop drilling.
|
|
322
|
+
|
|
323
|
+
```tsx
|
|
324
|
+
import { StateProvider, useStateHandler } from '@veams/status-quo/react';
|
|
325
|
+
|
|
326
|
+
const handler = useStateHandler(createUserStore, []);
|
|
327
|
+
|
|
328
|
+
return <StateProvider instance={handler}>{children}</StateProvider>;
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### `useProvidedStateHandler()`
|
|
332
|
+
|
|
333
|
+
Reads the nearest provider-scoped handler instance.
|
|
334
|
+
Use this as the low-level entry point when a descendant needs the raw handler for manual composition.
|
|
335
|
+
|
|
336
|
+
```ts
|
|
337
|
+
import { useProvidedStateHandler } from '@veams/status-quo/react';
|
|
338
|
+
|
|
339
|
+
const handler = useProvidedStateHandler<UserState, UserActions>();
|
|
340
|
+
```
|
|
341
|
+
|
|
245
342
|
### `useStateActions(handler)`
|
|
246
343
|
|
|
247
344
|
Returns actions from a handler without subscribing to state changes.
|
|
248
345
|
Use this in action-only components to avoid rerenders from state updates.
|
|
249
346
|
|
|
250
347
|
```ts
|
|
348
|
+
import { useStateActions, useStateHandler } from '@veams/status-quo/react';
|
|
349
|
+
|
|
251
350
|
const handler = useStateHandler(createUserStore, []);
|
|
252
351
|
const actions = useStateActions(handler);
|
|
253
352
|
```
|
|
254
353
|
|
|
354
|
+
### `useProvidedStateActions()`
|
|
355
|
+
|
|
356
|
+
Returns actions from the nearest `StateProvider` without subscribing to state.
|
|
357
|
+
Use this for command-only components inside a provider scope.
|
|
358
|
+
|
|
359
|
+
```ts
|
|
360
|
+
import { useProvidedStateActions } from '@veams/status-quo/react';
|
|
361
|
+
|
|
362
|
+
const actions = useProvidedStateActions<UserState, UserActions>();
|
|
363
|
+
```
|
|
364
|
+
|
|
255
365
|
### `useStateSubscription(source, selector?, isEqual?)`
|
|
256
366
|
|
|
257
367
|
Subscribes to either a handler instance or a singleton and returns `[selectedState, actions]`.
|
|
@@ -263,6 +373,8 @@ Subscribes to either a handler instance or a singleton and returns `[selectedSta
|
|
|
263
373
|
Full snapshot subscription:
|
|
264
374
|
|
|
265
375
|
```ts
|
|
376
|
+
import { useStateHandler, useStateSubscription } from '@veams/status-quo/react';
|
|
377
|
+
|
|
266
378
|
const handler = useStateHandler(createUserStore, []);
|
|
267
379
|
const [state, actions] = useStateSubscription(handler);
|
|
268
380
|
```
|
|
@@ -270,6 +382,8 @@ const [state, actions] = useStateSubscription(handler);
|
|
|
270
382
|
Selector subscription:
|
|
271
383
|
|
|
272
384
|
```ts
|
|
385
|
+
import { useStateSubscription } from '@veams/status-quo/react';
|
|
386
|
+
|
|
273
387
|
const [name, actions] = useStateSubscription(
|
|
274
388
|
handler,
|
|
275
389
|
(state) => state.user.name
|
|
@@ -279,6 +393,8 @@ const [name, actions] = useStateSubscription(
|
|
|
279
393
|
Selector with custom equality:
|
|
280
394
|
|
|
281
395
|
```ts
|
|
396
|
+
import { useStateSubscription } from '@veams/status-quo/react';
|
|
397
|
+
|
|
282
398
|
const [profile] = useStateSubscription(
|
|
283
399
|
handler,
|
|
284
400
|
(state) => state.user.profile,
|
|
@@ -289,12 +405,26 @@ const [profile] = useStateSubscription(
|
|
|
289
405
|
Singleton source:
|
|
290
406
|
|
|
291
407
|
```ts
|
|
408
|
+
import { useStateSubscription } from '@veams/status-quo/react';
|
|
409
|
+
|
|
292
410
|
const [session, actions] = useStateSubscription(SessionSingleton);
|
|
293
411
|
```
|
|
294
412
|
|
|
295
413
|
Lifecycle note for singleton sources:
|
|
296
414
|
- Consumers are ref-counted.
|
|
297
|
-
- The singleton instance is only destroyed when the last consumer unmounts and `destroyOnNoConsumers
|
|
415
|
+
- The singleton instance is only destroyed when the last consumer unmounts and `destroyOnNoConsumers === true`.
|
|
416
|
+
|
|
417
|
+
### `useProvidedStateSubscription(selector?, isEqual?)`
|
|
418
|
+
|
|
419
|
+
Subscribes to the nearest `StateProvider` instead of taking a source argument.
|
|
420
|
+
It supports full snapshots, selectors, and custom equality the same way `useStateSubscription` does.
|
|
421
|
+
|
|
422
|
+
```ts
|
|
423
|
+
import { useProvidedStateSubscription } from '@veams/status-quo/react';
|
|
424
|
+
|
|
425
|
+
const [state, actions] = useProvidedStateSubscription<UserState, UserActions>();
|
|
426
|
+
const [name] = useProvidedStateSubscription((state: UserState) => state.user.name);
|
|
427
|
+
```
|
|
298
428
|
|
|
299
429
|
### `useStateFactory(factory, selector?, isEqual?, params?)`
|
|
300
430
|
|
|
@@ -305,6 +435,8 @@ Shortcut API for `useStateHandler + useStateSubscription`.
|
|
|
305
435
|
- `useStateFactory(factory, selector, isEqual, params)`
|
|
306
436
|
|
|
307
437
|
```ts
|
|
438
|
+
import { useStateFactory } from '@veams/status-quo/react';
|
|
439
|
+
|
|
308
440
|
const [state, actions] = useStateFactory(createUserStore, []);
|
|
309
441
|
const [name] = useStateFactory(createUserStore, (state) => state.user.name, []);
|
|
310
442
|
const [profile] = useStateFactory(
|
|
@@ -320,6 +452,8 @@ const [profile] = useStateFactory(
|
|
|
320
452
|
Creates a shared singleton provider for a handler instance.
|
|
321
453
|
|
|
322
454
|
```ts
|
|
455
|
+
import { makeStateSingleton } from '@veams/status-quo';
|
|
456
|
+
|
|
323
457
|
const UserSingleton = makeStateSingleton(() => new UserStore());
|
|
324
458
|
```
|
|
325
459
|
|
|
@@ -327,16 +461,18 @@ Options:
|
|
|
327
461
|
|
|
328
462
|
```ts
|
|
329
463
|
type StateSingletonOptions = {
|
|
330
|
-
destroyOnNoConsumers?: boolean; // default:
|
|
464
|
+
destroyOnNoConsumers?: boolean; // default: false
|
|
331
465
|
};
|
|
332
466
|
```
|
|
333
467
|
|
|
334
|
-
- `
|
|
335
|
-
- `
|
|
468
|
+
- `false` (default): keep instance alive across periods with zero consumers
|
|
469
|
+
- `true`: destroy instance after last consumer unmounts
|
|
336
470
|
|
|
337
471
|
```ts
|
|
338
|
-
|
|
339
|
-
|
|
472
|
+
import { makeStateSingleton } from '@veams/status-quo';
|
|
473
|
+
|
|
474
|
+
const RouteScopedUserSingleton = makeStateSingleton(() => new UserStore(), {
|
|
475
|
+
destroyOnNoConsumers: true,
|
|
340
476
|
});
|
|
341
477
|
```
|
|
342
478
|
|
|
@@ -345,25 +481,50 @@ const PersistentUserSingleton = makeStateSingleton(() => new UserStore(), {
|
|
|
345
481
|
Shortcut API for `useStateSubscription(singleton, selector?, isEqual?)`.
|
|
346
482
|
|
|
347
483
|
```ts
|
|
484
|
+
import { useStateSingleton } from '@veams/status-quo/react';
|
|
485
|
+
|
|
348
486
|
const [state, actions] = useStateSingleton(UserSingleton);
|
|
349
487
|
const [name] = useStateSingleton(UserSingleton, (state) => state.user.name);
|
|
350
488
|
```
|
|
351
489
|
|
|
352
490
|
## Devtools
|
|
353
491
|
|
|
354
|
-
|
|
492
|
+
Status Quo supports the Redux DevTools browser extension on both `ObservableStateHandler` and `SignalStateHandler`.
|
|
493
|
+
|
|
494
|
+
Turn it on globally:
|
|
495
|
+
|
|
496
|
+
```ts
|
|
497
|
+
import { setupStatusQuo } from '@veams/status-quo';
|
|
498
|
+
|
|
499
|
+
setupStatusQuo({
|
|
500
|
+
devTools: {
|
|
501
|
+
enabled: true,
|
|
502
|
+
},
|
|
503
|
+
});
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
Override it per handler when needed:
|
|
355
507
|
|
|
356
508
|
```ts
|
|
357
509
|
class CounterStore extends ObservableStateHandler<CounterState, CounterActions> {
|
|
358
510
|
constructor() {
|
|
359
511
|
super({
|
|
360
512
|
initialState: { count: 0 },
|
|
361
|
-
options: { devTools: {
|
|
513
|
+
options: { devTools: { namespace: 'Counter' } },
|
|
362
514
|
});
|
|
363
515
|
}
|
|
364
516
|
}
|
|
365
517
|
```
|
|
366
518
|
|
|
519
|
+
Notes:
|
|
520
|
+
|
|
521
|
+
- `setupStatusQuo({ devTools: { enabled: true } })` enables Redux DevTools by default for handlers.
|
|
522
|
+
- `options.devTools` still overrides the global default per handler.
|
|
523
|
+
- `namespace` is optional. When omitted, Status Quo uses the handler class name.
|
|
524
|
+
- `setState(nextState, actionName)` sends the `actionName` to the Redux DevTools timeline.
|
|
525
|
+
- The current integration supports reset, commit, jump to state, and jump to action from the extension UI.
|
|
526
|
+
- If the extension is not installed, Status Quo logs that and continues without a devtools connection.
|
|
527
|
+
|
|
367
528
|
## Cleanup
|
|
368
529
|
|
|
369
530
|
Handlers expose `subscribe`, `getSnapshot`, and `destroy` for custom integrations:
|
|
@@ -419,7 +580,7 @@ Protected helpers:
|
|
|
419
580
|
|
|
420
581
|
- `getStateValue(): S` (abstract)
|
|
421
582
|
- `setStateValue(next: S): void` (abstract)
|
|
422
|
-
- `initDevTools(options?: { enabled?: boolean; namespace
|
|
583
|
+
- `initDevTools(options?: { enabled?: boolean; namespace?: string }): void`
|
|
423
584
|
- `bindSubscribable<T>(service: { subscribe: (listener: (value: T) => void) => () => void; getSnapshot?: () => T }, onChange: (value: T) => void, selector?: (value: T) => T, isEqual?: (current: T, next: T) => boolean): void`
|
|
424
585
|
- `bindSubscribable<T, Sel>(service: { subscribe: (listener: (value: T) => void) => () => void; getSnapshot?: () => T }, onChange: (value: Sel) => void, selector: (value: T) => Sel, isEqual?: (current: Sel, next: Sel) => boolean): void`
|
|
425
586
|
- Registers the subscription on `this.subscriptions` and invokes `onChange` with the current snapshot when available.
|
|
@@ -439,7 +600,7 @@ protected constructor({
|
|
|
439
600
|
}: {
|
|
440
601
|
initialState: S;
|
|
441
602
|
options?: {
|
|
442
|
-
devTools?: { enabled?: boolean; namespace
|
|
603
|
+
devTools?: { enabled?: boolean; namespace?: string };
|
|
443
604
|
distinct?: {
|
|
444
605
|
enabled?: boolean;
|
|
445
606
|
comparator?: (previous: S, next: S) => boolean;
|
|
@@ -451,15 +612,15 @@ protected constructor({
|
|
|
451
612
|
|
|
452
613
|
Public methods:
|
|
453
614
|
|
|
454
|
-
- `
|
|
455
|
-
- `
|
|
456
|
-
- `getObservable(key: keyof S): Observable<S[keyof S]>`
|
|
615
|
+
- `getObservable(options?: { useDistinctUntilChanged?: boolean }): Observable<S>`
|
|
616
|
+
- `getObservableItem(key: keyof S): Observable<S[keyof S]>`
|
|
457
617
|
- `subscribe(listener: () => void): () => void`
|
|
458
618
|
- `subscribe(listener: (value: S) => void): () => void`
|
|
459
619
|
|
|
460
620
|
Notes:
|
|
461
621
|
- The observable stream uses `distinctUntilChanged` by default.
|
|
462
622
|
- Distinct behavior can be configured globally via `setupStatusQuo` or per handler via `options.distinct`.
|
|
623
|
+
- Devtools can be enabled globally via `setupStatusQuo({ devTools: { enabled: true } })` or overridden per handler via `options.devTools`.
|
|
463
624
|
- `subscribe` fires immediately with the current snapshot and then on subsequent changes.
|
|
464
625
|
- Subscribers receive the next state snapshot as a callback argument.
|
|
465
626
|
|
|
@@ -476,7 +637,7 @@ protected constructor({
|
|
|
476
637
|
}: {
|
|
477
638
|
initialState: S;
|
|
478
639
|
options?: {
|
|
479
|
-
devTools?: { enabled?: boolean; namespace
|
|
640
|
+
devTools?: { enabled?: boolean; namespace?: string };
|
|
480
641
|
distinct?: {
|
|
481
642
|
enabled?: boolean;
|
|
482
643
|
comparator?: (previous: S, next: S) => boolean;
|
|
@@ -495,12 +656,16 @@ Public methods:
|
|
|
495
656
|
Notes:
|
|
496
657
|
- Distinct behavior defaults to enabled.
|
|
497
658
|
- Configure it globally via `setupStatusQuo` or per handler via `options.distinct`.
|
|
659
|
+
- Devtools can be enabled globally via `setupStatusQuo({ devTools: { enabled: true } })` or overridden per handler via `options.devTools`.
|
|
498
660
|
- `useDistinctUntilChanged` remains available as a shorthand enable/disable override.
|
|
499
661
|
|
|
500
662
|
### `setupStatusQuo`
|
|
501
663
|
|
|
502
664
|
```ts
|
|
503
665
|
type StatusQuoConfig = {
|
|
666
|
+
devTools?: {
|
|
667
|
+
enabled?: boolean;
|
|
668
|
+
};
|
|
504
669
|
distinct?: {
|
|
505
670
|
enabled?: boolean;
|
|
506
671
|
comparator?: (previous: unknown, next: unknown) => boolean;
|
|
@@ -514,7 +679,7 @@ function setupStatusQuo(config?: StatusQuoConfig): void
|
|
|
514
679
|
|
|
515
680
|
```ts
|
|
516
681
|
type StateSingletonOptions = {
|
|
517
|
-
destroyOnNoConsumers?: boolean; // default:
|
|
682
|
+
destroyOnNoConsumers?: boolean; // default: false
|
|
518
683
|
};
|
|
519
684
|
|
|
520
685
|
function makeStateSingleton<S, A>(
|
|
@@ -526,13 +691,22 @@ function makeStateSingleton<S, A>(
|
|
|
526
691
|
```
|
|
527
692
|
|
|
528
693
|
Lifecycle behavior:
|
|
529
|
-
- `destroyOnNoConsumers:
|
|
530
|
-
- `destroyOnNoConsumers:
|
|
694
|
+
- `destroyOnNoConsumers: false` (default): keep the same singleton instance alive when no component is subscribed.
|
|
695
|
+
- `destroyOnNoConsumers: true`: destroy and recreate singleton instances with mount lifecycle.
|
|
531
696
|
|
|
532
697
|
### Hooks
|
|
533
698
|
|
|
699
|
+
- React entrypoint: `@veams/status-quo/react`
|
|
534
700
|
- `useStateHandler<V, A, P extends unknown[]>(factory: (...args: P) => StateSubscriptionHandler<V, A>, params?: P)`
|
|
535
701
|
- Returns `StateSubscriptionHandler<V, A>`.
|
|
702
|
+
- `StateProvider<V, A>({ instance }: { instance: StateSubscriptionHandler<V, A> })`
|
|
703
|
+
- Shares a handler instance with a subtree.
|
|
704
|
+
- `useProvidedStateHandler<V, A>()`
|
|
705
|
+
- Returns `StateSubscriptionHandler<V, A>`.
|
|
706
|
+
- `useProvidedStateActions<V, A>()`
|
|
707
|
+
- Returns `A`.
|
|
708
|
+
- `useProvidedStateSubscription<V, A, Sel = V>(selector?: (state: V) => Sel, isEqual?: (current: Sel, next: Sel) => boolean)`
|
|
709
|
+
- Returns `[state, actions]`.
|
|
536
710
|
- `useStateActions<V, A>(handler: StateSubscriptionHandler<V, A>)`
|
|
537
711
|
- Returns `A`.
|
|
538
712
|
- `useStateSubscription<V, A, Sel = V>(source: StateSubscriptionHandler<V, A> | StateSingleton<V, A>, selector?: (state: V) => Sel, isEqual?: (current: Sel, next: Sel) => boolean)`
|
|
@@ -548,7 +722,9 @@ From pre-1.0 releases:
|
|
|
548
722
|
|
|
549
723
|
1. Rename `StateHandler` -> `ObservableStateHandler`.
|
|
550
724
|
2. Implement `subscribe()` and `getSnapshot()` on custom handlers.
|
|
551
|
-
3. Replace `
|
|
552
|
-
4.
|
|
725
|
+
3. Replace `getStateAsObservable()` with `getObservable()`.
|
|
726
|
+
4. Replace `getStateItemAsObservable()` and keyed `getObservable(key)` with `getObservableItem(key)`.
|
|
727
|
+
5. Move React hooks and `StateProvider` imports from `@veams/status-quo` or `@veams/status-quo/hooks` to `@veams/status-quo/react`.
|
|
728
|
+
6. Update devtools config:
|
|
553
729
|
- From: `super({ initialState, devTools: { ... } })`
|
|
554
730
|
- To: `super({ initialState, options: { devTools: { ... } } })`
|
|
@@ -3,18 +3,31 @@ export type DistinctOptions<T = unknown> = {
|
|
|
3
3
|
enabled?: boolean;
|
|
4
4
|
comparator?: DistinctComparator<T>;
|
|
5
5
|
};
|
|
6
|
+
export type DevToolsOptions = {
|
|
7
|
+
enabled?: boolean;
|
|
8
|
+
namespace?: string;
|
|
9
|
+
};
|
|
10
|
+
export type GlobalDevToolsOptions = {
|
|
11
|
+
enabled?: boolean;
|
|
12
|
+
};
|
|
6
13
|
export type StatusQuoConfig<T = unknown> = {
|
|
14
|
+
devTools?: GlobalDevToolsOptions;
|
|
7
15
|
distinct?: DistinctOptions<T>;
|
|
8
16
|
};
|
|
9
17
|
type ResolvedDistinctOptions<T = unknown> = {
|
|
10
18
|
enabled: boolean;
|
|
11
19
|
comparator: DistinctComparator<T>;
|
|
12
20
|
};
|
|
21
|
+
type ResolvedDevToolsOptions = {
|
|
22
|
+
enabled: boolean;
|
|
23
|
+
};
|
|
13
24
|
type ResolvedStatusQuoConfig = {
|
|
25
|
+
devTools: ResolvedDevToolsOptions;
|
|
14
26
|
distinct: ResolvedDistinctOptions;
|
|
15
27
|
};
|
|
16
28
|
export declare function setupStatusQuo<T = unknown>(config?: StatusQuoConfig<T>): void;
|
|
17
29
|
export declare function resolveDistinctOptions<T>(options?: DistinctOptions<T>, useDistinctUntilChanged?: boolean): ResolvedDistinctOptions<T>;
|
|
30
|
+
export declare function resolveDevToolsOptions(options?: DevToolsOptions): ResolvedDevToolsOptions;
|
|
18
31
|
export declare function getStatusQuoConfig(): ResolvedStatusQuoConfig;
|
|
19
32
|
/** @internal testing helper */
|
|
20
33
|
export declare function resetStatusQuoForTests(): void;
|