@genesislcap/foundation-redux 14.310.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/README.md +185 -0
- package/dist/dts/index.d.ts +3 -0
- package/dist/dts/index.d.ts.map +1 -0
- package/dist/dts/store.d.ts +29 -0
- package/dist/dts/store.d.ts.map +1 -0
- package/dist/dts/types.d.ts +176 -0
- package/dist/dts/types.d.ts.map +1 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/store.js +197 -0
- package/dist/esm/types.js +1 -0
- package/dist/foundation-redux.api.json +957 -0
- package/dist/foundation-redux.d.ts +222 -0
- package/dist/tsdoc-metadata.json +11 -0
- package/docs/.gitattributes +2 -0
- package/docs/api/foundation-redux.actionsfromslices.md +36 -0
- package/docs/api/foundation-redux.createstore.md +89 -0
- package/docs/api/foundation-redux.md +139 -0
- package/docs/api/foundation-redux.rootstatefromslices.md +32 -0
- package/docs/api/foundation-redux.selectorsfromslices.md +40 -0
- package/docs/api/foundation-redux.slice.actions.md +13 -0
- package/docs/api/foundation-redux.slice.md +131 -0
- package/docs/api/foundation-redux.slice.name.md +13 -0
- package/docs/api/foundation-redux.slice.reducer.md +13 -0
- package/docs/api/foundation-redux.slice.selectors.md +13 -0
- package/docs/api/foundation-redux.slicearray.md +27 -0
- package/docs/api/foundation-redux.storereturn.md +36 -0
- package/docs/api/foundation-redux.thunkdispatch.md +28 -0
- package/docs/api/index.md +30 -0
- package/docs/api-report.md.api.md +87 -0
- package/license.txt +46 -0
- package/package.json +61 -0
package/README.md
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# Genesis Foundation Redux
|
|
2
|
+
|
|
3
|
+
[](https://lerna.js.org/)
|
|
4
|
+
[](https://www.typescriptlang.org/)
|
|
5
|
+
|
|
6
|
+
## [API Docs](./docs/api/index.md)
|
|
7
|
+
|
|
8
|
+
`foundation-redux` provides Redux Toolkit integration for Genesis components, offering a clean and efficient way to manage application state using the proven Redux pattern.
|
|
9
|
+
|
|
10
|
+
## Background
|
|
11
|
+
|
|
12
|
+
This package was created to provide an alternative to `foundation-store` by leveraging the power and ecosystem of Redux Toolkit. While `foundation-store` implements a Redux-like pattern within the Genesis context, `foundation-redux` provides direct bindings to Redux itself, allowing developers to benefit from:
|
|
13
|
+
|
|
14
|
+
- Vast Redux documentation and ecosystem
|
|
15
|
+
- Redux DevTools integration
|
|
16
|
+
- Proven patterns and best practices
|
|
17
|
+
- Extensive community support
|
|
18
|
+
- Built-in immutability handling
|
|
19
|
+
|
|
20
|
+
## Why Choose foundation-redux over foundation-store?
|
|
21
|
+
|
|
22
|
+
### Reduced Boilerplate
|
|
23
|
+
Foundation store requires manual immutability handling and custom event setup, while Redux Toolkit handles immutability automatically.
|
|
24
|
+
|
|
25
|
+
**Foundation store example:**
|
|
26
|
+
```typescript
|
|
27
|
+
this.createListener<DeleteViewDerivedFieldPayload>(
|
|
28
|
+
StoreEvents.DeleteViewDerivedField,
|
|
29
|
+
({ view, field }) => {
|
|
30
|
+
const { [field]: fieldToDelete, ...rest } =
|
|
31
|
+
this.commit.genesisCreateConfig.views.views[view]?.derivedFields || {};
|
|
32
|
+
if (fieldToDelete) {
|
|
33
|
+
this.commit.genesisCreateConfig = {
|
|
34
|
+
...this.genesisCreateConfig,
|
|
35
|
+
views: {
|
|
36
|
+
views: {
|
|
37
|
+
...this.genesisCreateConfig.views.views,
|
|
38
|
+
[view]: {
|
|
39
|
+
...this.genesisCreateConfig.views.views[view],
|
|
40
|
+
derivedFields: {
|
|
41
|
+
...rest,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
);
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Redux Toolkit example:**
|
|
53
|
+
```typescript
|
|
54
|
+
deleteDerivedField(state, { payload: { view, field } }) {
|
|
55
|
+
delete state[view].derivedFields[field];
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Ecosystem Benefits
|
|
60
|
+
- **Redux DevTools**: Built-in debugging and time-travel debugging
|
|
61
|
+
- **Middleware**: Extensive middleware ecosystem
|
|
62
|
+
- **Entity Adapters**: Auto-generated CRUD actions and selectors
|
|
63
|
+
- **Serialization**: Built-in support for local storage and hydration
|
|
64
|
+
|
|
65
|
+
## Installation
|
|
66
|
+
|
|
67
|
+
To enable this module in your application, follow the steps below.
|
|
68
|
+
|
|
69
|
+
1. Add `@genesislcap/foundation-redux` as a dependency in your `package.json` file. Whenever you change the dependencies of your project, ensure you run the `$ npm run bootstrap` command again.
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
...
|
|
74
|
+
"dependencies": {
|
|
75
|
+
...
|
|
76
|
+
"@genesislcap/foundation-redux": "latest"
|
|
77
|
+
...
|
|
78
|
+
},
|
|
79
|
+
...
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Usage
|
|
84
|
+
|
|
85
|
+
### Basic Store Setup
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { createStore } from '@genesislcap/foundation-redux';
|
|
89
|
+
import { createSlice } from '@reduxjs/toolkit';
|
|
90
|
+
import { customElement, GenesisElement, observable } from '@genesislcap/web-core';
|
|
91
|
+
|
|
92
|
+
// Define your slices
|
|
93
|
+
const userSlice = createSlice({
|
|
94
|
+
name: 'user',
|
|
95
|
+
initialState: { name: '', email: '' },
|
|
96
|
+
reducers: {
|
|
97
|
+
setUser: (state, action) => {
|
|
98
|
+
state.name = action.payload.name;
|
|
99
|
+
state.email = action.payload.email;
|
|
100
|
+
},
|
|
101
|
+
clearUser: (state) => {
|
|
102
|
+
state.name = '';
|
|
103
|
+
state.email = '';
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
selectors: {
|
|
107
|
+
getUserName: (state) => state.name,
|
|
108
|
+
getUserEmail: (state) => state.email,
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Create the store
|
|
113
|
+
const { store, actions, selectors } = createStore([userSlice], {});
|
|
114
|
+
|
|
115
|
+
@customElement({
|
|
116
|
+
name: 'user-component',
|
|
117
|
+
template: html`<div>Hello ${(x) => x.userName}!</div>`,
|
|
118
|
+
})
|
|
119
|
+
export class UserComponent extends GenesisElement {
|
|
120
|
+
@observable userName = '';
|
|
121
|
+
|
|
122
|
+
connectedCallback() {
|
|
123
|
+
super.connectedCallback();
|
|
124
|
+
|
|
125
|
+
// Subscribe to store changes
|
|
126
|
+
store.subscribeKey(
|
|
127
|
+
(state) => state.user.name,
|
|
128
|
+
(state) => {
|
|
129
|
+
this.userName = state.user.name;
|
|
130
|
+
}
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
handleLogin() {
|
|
135
|
+
actions.user.setUser({ name: 'John Doe', email: 'john@example.com' });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Advanced Features
|
|
141
|
+
|
|
142
|
+
- **Thunk Actions**: Support for async operations
|
|
143
|
+
- **Entity Adapters**: Auto-generated CRUD operations
|
|
144
|
+
- **DevTools**: Full Redux DevTools integration
|
|
145
|
+
- **Middleware**: Custom middleware support
|
|
146
|
+
- **Local Storage**: Built-in persistence helpers
|
|
147
|
+
|
|
148
|
+
## API Reference
|
|
149
|
+
|
|
150
|
+
### `createStore(slices, preloadedState)`
|
|
151
|
+
|
|
152
|
+
Creates a Redux store with Genesis component integration.
|
|
153
|
+
|
|
154
|
+
**Parameters:**
|
|
155
|
+
- `slices`: Array of Redux Toolkit slices
|
|
156
|
+
- `preloadedState`: Initial state for the store
|
|
157
|
+
|
|
158
|
+
**Returns:**
|
|
159
|
+
- `store`: Proxied state object for reactive updates
|
|
160
|
+
- `actions`: Bound action creators
|
|
161
|
+
- `selectors`: Bound selectors
|
|
162
|
+
- `dispatch`: Redux dispatch function
|
|
163
|
+
- `subscribe`: Store subscription function
|
|
164
|
+
- `subscribeKey`: Key-based subscription for performance
|
|
165
|
+
- `notify`: Manual notification trigger
|
|
166
|
+
|
|
167
|
+
### Store Integration
|
|
168
|
+
|
|
169
|
+
The store automatically integrates with Genesis's Observable system, providing reactive updates when state changes. Components can subscribe to specific state changes using `subscribeKey` for optimal performance.
|
|
170
|
+
|
|
171
|
+
## Migration from foundation-store
|
|
172
|
+
|
|
173
|
+
If you're currently using `foundation-store`, migration to `foundation-redux` involves:
|
|
174
|
+
|
|
175
|
+
1. **Replace store creation**: Use `createStore` instead of store fragments
|
|
176
|
+
2. **Convert event handlers**: Replace custom events with Redux actions
|
|
177
|
+
3. **Update subscriptions**: Use `subscribeKey` instead of binding observers
|
|
178
|
+
4. **Simplify state updates**: Let Redux Toolkit handle immutability
|
|
179
|
+
|
|
180
|
+
## License
|
|
181
|
+
|
|
182
|
+
Note: this project provides front-end dependencies and uses licensed components listed in the next section; thus, licenses for those components are required during development. Contact [Genesis Global](https://genesis.global/contact-us/) for more details.
|
|
183
|
+
|
|
184
|
+
### Licensed components
|
|
185
|
+
Genesis low-code platform
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { StoreReturn, RootStateFromSlices, SliceArray } from './types';
|
|
2
|
+
export { createSlice } from '@reduxjs/toolkit';
|
|
3
|
+
/**
|
|
4
|
+
* Creates a Redux store with FAST component integration.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* This function creates a Redux store that integrates seamlessly with FAST components.
|
|
8
|
+
* It automatically binds actions and selectors, provides subscription methods, and handles
|
|
9
|
+
* FAST Observable notifications.
|
|
10
|
+
*
|
|
11
|
+
* @param slices - Array of slices to combine into the store
|
|
12
|
+
* @param preloadedState - Initial state that matches the slice structure
|
|
13
|
+
* @returns Object containing store, actions, selectors, and utility functions
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* const { store, actions, selectors, subscribeKey } = createStore(
|
|
18
|
+
* [userSlice, cartSlice],
|
|
19
|
+
* {
|
|
20
|
+
* user: { name: '', email: '' },
|
|
21
|
+
* cart: { items: [], total: 0 }
|
|
22
|
+
* }
|
|
23
|
+
* );
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @public
|
|
27
|
+
*/
|
|
28
|
+
export declare const createStore: <S extends SliceArray>(slices: S, preloadedState: RootStateFromSlices<S>) => StoreReturn<S>;
|
|
29
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/store.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,WAAW,EACX,mBAAmB,EAEnB,UAAU,EAEX,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAyL/C;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,SAAS,UAAU,EAC9C,QAAQ,CAAC,EACT,gBAAgB,mBAAmB,CAAC,CAAC,CAAC,KACrC,WAAW,CAAC,CAAC,CAoBf,CAAC"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { Action, Reducer, ReducersMapObject, ThunkAction } from '@reduxjs/toolkit';
|
|
2
|
+
export type { PayloadAction } from '@reduxjs/toolkit';
|
|
3
|
+
/**
|
|
4
|
+
* Represents a Redux slice with actions and selectors.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* A slice contains the reducer, actions, and selectors for a specific domain of your application state.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const userSlice: Slice = {
|
|
12
|
+
* name: 'user',
|
|
13
|
+
* reducer: userReducer,
|
|
14
|
+
* actions: { setUser, logout },
|
|
15
|
+
* selectors: { getUser, isLoggedIn }
|
|
16
|
+
* };
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @public
|
|
20
|
+
*/
|
|
21
|
+
export interface Slice<State = any> {
|
|
22
|
+
/** The unique name of the slice */
|
|
23
|
+
name: string;
|
|
24
|
+
/** The Redux reducer function for this slice */
|
|
25
|
+
reducer: Reducer<State, Action>;
|
|
26
|
+
/** Action creators for this slice */
|
|
27
|
+
actions: Record<string, (payload: any) => Action>;
|
|
28
|
+
/** Selector functions for this slice */
|
|
29
|
+
selectors: Record<string, (...args: any[]) => any>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* An array of slices that will be combined into a single store.
|
|
33
|
+
*
|
|
34
|
+
* @remarks
|
|
35
|
+
* This type represents the collection of slices that will be used to create a Redux store.
|
|
36
|
+
* Each slice in the array will become a top-level key in the store's state.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* const slices: SliceArray = [userSlice, cartSlice, preferencesSlice];
|
|
41
|
+
* const { store } = createStore(slices, initialState);
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* @public
|
|
45
|
+
*/
|
|
46
|
+
export type SliceArray = Slice[];
|
|
47
|
+
/**
|
|
48
|
+
* Extracts the root state type from an array of slices.
|
|
49
|
+
*
|
|
50
|
+
* @remarks
|
|
51
|
+
* This utility type maps over the slice names and extracts the state type for each slice.
|
|
52
|
+
* The resulting type represents the complete state structure of your Redux store.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```typescript
|
|
56
|
+
* const slices = [userSlice, cartSlice];
|
|
57
|
+
* type RootState = RootStateFromSlices<typeof slices>;
|
|
58
|
+
* // Result: { user: UserState, cart: CartState }
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* @public
|
|
62
|
+
*/
|
|
63
|
+
export type RootStateFromSlices<S extends SliceArray> = {
|
|
64
|
+
[K in S[number]['name']]: Extract<S[number], {
|
|
65
|
+
name: K;
|
|
66
|
+
}> extends Slice<infer State> ? State : never;
|
|
67
|
+
};
|
|
68
|
+
type FirstIndex = 0;
|
|
69
|
+
type FirstParameter<T> = T extends (...args: infer P) => any ? P[FirstIndex] : never;
|
|
70
|
+
/**
|
|
71
|
+
* Extracts the bound actions type from an array of slices.
|
|
72
|
+
*
|
|
73
|
+
* @remarks
|
|
74
|
+
* This utility type creates a nested object structure where each slice name maps to its actions.
|
|
75
|
+
* Actions are bound to the store and automatically dispatch when called.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```typescript
|
|
79
|
+
* const { actions } = createStore([userSlice, cartSlice], initialState);
|
|
80
|
+
* // actions.user.setUser({ name: 'John' })
|
|
81
|
+
* // actions.cart.addItem({ id: 'item1', quantity: 1 })
|
|
82
|
+
* ```
|
|
83
|
+
*
|
|
84
|
+
* @public
|
|
85
|
+
*/
|
|
86
|
+
export type ActionsFromSlices<S extends SliceArray> = {
|
|
87
|
+
[K in S[number]['name']]: {
|
|
88
|
+
[ActionKey in keyof Extract<S[number], {
|
|
89
|
+
name: K;
|
|
90
|
+
}>['actions']]: (payload: FirstParameter<Extract<S[number], {
|
|
91
|
+
name: K;
|
|
92
|
+
}>['actions'][ActionKey]>) => void;
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
type RemoveFirstParameter<T> = T extends (arg1: any, ...rest: infer Rest) => any ? Rest : never;
|
|
96
|
+
/**
|
|
97
|
+
* Extracts the bound selectors type from an array of slices.
|
|
98
|
+
*
|
|
99
|
+
* @remarks
|
|
100
|
+
* This utility type creates a nested object structure where each slice name maps to its selectors.
|
|
101
|
+
* Selectors are bound to the store and can access the current state.
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```typescript
|
|
105
|
+
* const { selectors } = createStore([userSlice, cartSlice], initialState);
|
|
106
|
+
* // const userName = selectors.user.getUserName()
|
|
107
|
+
* // const cartTotal = selectors.cart.getTotal()
|
|
108
|
+
* ```
|
|
109
|
+
*
|
|
110
|
+
* @public
|
|
111
|
+
*/
|
|
112
|
+
export type SelectorsFromSlices<S extends SliceArray> = {
|
|
113
|
+
[K in S[number]['name']]: {
|
|
114
|
+
[SelectorKey in keyof Extract<S[number], {
|
|
115
|
+
name: K;
|
|
116
|
+
}>['selectors']]: (...payload: RemoveFirstParameter<Extract<S[number], {
|
|
117
|
+
name: K;
|
|
118
|
+
}>['selectors'][SelectorKey]>) => ReturnType<Extract<S[number], {
|
|
119
|
+
name: K;
|
|
120
|
+
}>['selectors'][SelectorKey]> extends void ? any : ReturnType<Extract<S[number], {
|
|
121
|
+
name: K;
|
|
122
|
+
}>['selectors'][SelectorKey]>;
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* Type for thunk actions that can be dispatched to the store.
|
|
127
|
+
*
|
|
128
|
+
* @remarks
|
|
129
|
+
* This type represents thunk actions that can access the store's dispatch and getState functions.
|
|
130
|
+
* Thunks are useful for handling asynchronous operations and complex logic.
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* ```typescript
|
|
134
|
+
* const fetchUserThunk: ThunkDispatch<typeof slices> = (dispatch, getState) => {
|
|
135
|
+
* fetch('/api/user').then(user => dispatch(actions.user.setUser(user)));
|
|
136
|
+
* };
|
|
137
|
+
* ```
|
|
138
|
+
*
|
|
139
|
+
* @public
|
|
140
|
+
*/
|
|
141
|
+
export type ThunkDispatch<S extends SliceArray> = ThunkAction<void, RootStateFromSlices<S>, any, Action>;
|
|
142
|
+
/**
|
|
143
|
+
* The return type of the createStore function.
|
|
144
|
+
*
|
|
145
|
+
* @remarks
|
|
146
|
+
* This type represents all the utilities and functions returned when creating a store.
|
|
147
|
+
* It provides access to the store state, actions, selectors, and subscription methods.
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* ```typescript
|
|
151
|
+
* const { store, actions, selectors, subscribeKey } = createStore(slices, initialState);
|
|
152
|
+
* ```
|
|
153
|
+
*
|
|
154
|
+
* @public
|
|
155
|
+
*/
|
|
156
|
+
export type StoreReturn<S extends SliceArray> = {
|
|
157
|
+
/** Proxy object for accessing slice state */
|
|
158
|
+
store: RootStateFromSlices<S>;
|
|
159
|
+
/** Bound actions for each slice */
|
|
160
|
+
actions: ActionsFromSlices<S>;
|
|
161
|
+
/** Bound selectors for each slice */
|
|
162
|
+
selectors: SelectorsFromSlices<S>;
|
|
163
|
+
/** The root reducer combining all slices */
|
|
164
|
+
rootReducer: ReducersMapObject<RootStateFromSlices<S>>;
|
|
165
|
+
/** Performance-optimized subscription function for specific values */
|
|
166
|
+
subscribeKey: (checkerFn: (s: RootStateFromSlices<S>) => string, callbackFn: (s: RootStateFromSlices<S>) => void) => void;
|
|
167
|
+
/** Function to dispatch actions or thunks */
|
|
168
|
+
dispatch: (action: Action | ThunkDispatch<S>) => void;
|
|
169
|
+
/** Function to notify FAST components of slice changes */
|
|
170
|
+
notify: (sliceName: keyof RootStateFromSlices<S>) => void;
|
|
171
|
+
/** General subscription function for all state changes */
|
|
172
|
+
subscribe: (cb: (state: RootStateFromSlices<S>) => void) => void;
|
|
173
|
+
/** Function to get the current state */
|
|
174
|
+
getState: () => RootStateFromSlices<S>;
|
|
175
|
+
};
|
|
176
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { isDev } from '@genesislcap/foundation-utils';
|
|
2
|
+
import { Observable } from '@genesislcap/web-core';
|
|
3
|
+
import { configureStore } from '@reduxjs/toolkit';
|
|
4
|
+
export { createSlice } from '@reduxjs/toolkit';
|
|
5
|
+
/**
|
|
6
|
+
* Internal store class that wraps Redux store with FAST Observable integration.
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* This class provides the core functionality for integrating Redux with FAST components.
|
|
10
|
+
* It handles subscriptions, notifications, and provides a clean API for actions and selectors.
|
|
11
|
+
*
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
14
|
+
class ObservableStore {
|
|
15
|
+
/**
|
|
16
|
+
* Creates a new ObservableStore instance.
|
|
17
|
+
*
|
|
18
|
+
* @param slices - Array of slices to combine into the store
|
|
19
|
+
* @param preloadedState - Initial state for the store
|
|
20
|
+
*/
|
|
21
|
+
constructor(slices, preloadedState) {
|
|
22
|
+
/**
|
|
23
|
+
* Gets the underlying Redux store instance.
|
|
24
|
+
*
|
|
25
|
+
* @returns The Redux store instance
|
|
26
|
+
*/
|
|
27
|
+
this.getReduxStore = () => this._store;
|
|
28
|
+
/**
|
|
29
|
+
* Dispatches actions or thunks to the store.
|
|
30
|
+
*
|
|
31
|
+
* @remarks
|
|
32
|
+
* Currently, we do not support extra arguments in thunk actions.
|
|
33
|
+
* This is because they are not used in our codebase.
|
|
34
|
+
* We can add support for extra arguments later if needed for middleware.
|
|
35
|
+
* https://redux-toolkit.js.org/api/getDefaultMiddleware#customizing-the-included-middleware
|
|
36
|
+
*
|
|
37
|
+
* @param action - The action or thunk to dispatch
|
|
38
|
+
*/
|
|
39
|
+
this.dispatch = (action) => typeof action === 'function'
|
|
40
|
+
? action(this._store.dispatch, this._store.getState, undefined)
|
|
41
|
+
: this._store.dispatch(action);
|
|
42
|
+
/**
|
|
43
|
+
* Notifies FAST components of changes to a specific slice.
|
|
44
|
+
*
|
|
45
|
+
* @param sliceName - The name of the slice that changed
|
|
46
|
+
*/
|
|
47
|
+
this.notify = (sliceName) => Observable.notify(this, sliceName);
|
|
48
|
+
/**
|
|
49
|
+
* Gets the root reducer that combines all slices.
|
|
50
|
+
*
|
|
51
|
+
* @returns The combined reducer object
|
|
52
|
+
*/
|
|
53
|
+
this.getRootReducer = () => this._rootReducer;
|
|
54
|
+
/**
|
|
55
|
+
* Subscribes to all state changes in the store.
|
|
56
|
+
*
|
|
57
|
+
* @param cb - Callback function called whenever state changes
|
|
58
|
+
*/
|
|
59
|
+
this.subscribe = (cb) => this._store.subscribe(() => {
|
|
60
|
+
cb(this._store.getState());
|
|
61
|
+
});
|
|
62
|
+
/**
|
|
63
|
+
* Subscribes to changes of a specific value in the store.
|
|
64
|
+
*
|
|
65
|
+
* @remarks
|
|
66
|
+
* This method is performance-optimized and only triggers the callback when the specific value changes.
|
|
67
|
+
* The checkerFn should return a string for comparison purposes.
|
|
68
|
+
*
|
|
69
|
+
* @param checkerFn - Function that extracts the value to watch (must return string)
|
|
70
|
+
* @param callbackFn - Callback function called when the watched value changes
|
|
71
|
+
*/
|
|
72
|
+
this.subscribeKey = (checkerFn, callbackFn) => {
|
|
73
|
+
let cache = checkerFn(this._store.getState());
|
|
74
|
+
this.subscribe(() => {
|
|
75
|
+
const newState = checkerFn(this._store.getState());
|
|
76
|
+
if (cache === newState)
|
|
77
|
+
return;
|
|
78
|
+
cache = newState;
|
|
79
|
+
callbackFn(this._store.getState());
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Gets a specific slice from the store and tracks it for FAST Observable.
|
|
84
|
+
*
|
|
85
|
+
* @param name - The name of the slice to retrieve
|
|
86
|
+
* @returns The slice state
|
|
87
|
+
*/
|
|
88
|
+
this.getSlice = (name) => {
|
|
89
|
+
Observable.track(this, name);
|
|
90
|
+
return this._store.getState()[name];
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Creates bound actions for all slices.
|
|
94
|
+
*
|
|
95
|
+
* @remarks
|
|
96
|
+
* Actions are automatically bound to the store and will dispatch when called.
|
|
97
|
+
* Each action also triggers a notification to FAST components.
|
|
98
|
+
*
|
|
99
|
+
* @returns Object containing bound actions organized by slice name
|
|
100
|
+
* @throws Error if action names are not unique across slices
|
|
101
|
+
*/
|
|
102
|
+
this.getActions = () => this._slices.reduce((acc, slice) => {
|
|
103
|
+
Object.keys(slice.actions).forEach((key) => {
|
|
104
|
+
if (acc[key]) {
|
|
105
|
+
throw new Error(`Action name not unique: ${key}`);
|
|
106
|
+
}
|
|
107
|
+
acc[slice.name] = Object.assign(Object.assign({}, acc[slice.name]), { [key]: (payload) => {
|
|
108
|
+
this.dispatch(slice.actions[key](payload));
|
|
109
|
+
this.notify(slice.name);
|
|
110
|
+
} });
|
|
111
|
+
});
|
|
112
|
+
return acc;
|
|
113
|
+
}, {});
|
|
114
|
+
/**
|
|
115
|
+
* Creates bound selectors for all slices.
|
|
116
|
+
*
|
|
117
|
+
* @remarks
|
|
118
|
+
* Selectors are bound to the store and can access the current state.
|
|
119
|
+
* They receive the full state object and any additional parameters.
|
|
120
|
+
*
|
|
121
|
+
* @returns Object containing bound selectors organized by slice name
|
|
122
|
+
* @throws Error if selector names are not unique across slices
|
|
123
|
+
*/
|
|
124
|
+
this.getSelectors = () => this._slices.reduce((acc, slice) => {
|
|
125
|
+
Object.keys(slice.selectors).forEach((key) => {
|
|
126
|
+
var _a;
|
|
127
|
+
if ((_a = acc[slice.name]) === null || _a === void 0 ? void 0 : _a[key]) {
|
|
128
|
+
throw new Error(`Selector name not unique: ${key}`);
|
|
129
|
+
}
|
|
130
|
+
if (!acc[slice.name]) {
|
|
131
|
+
acc[slice.name] = {};
|
|
132
|
+
}
|
|
133
|
+
acc[slice.name] = Object.assign(Object.assign({}, acc[slice.name]), { [key]: (...params) => {
|
|
134
|
+
const sliceState = this.getSlice(slice.name);
|
|
135
|
+
const fullState = Object.assign(Object.assign({}, this._store.getState()), { [slice.name]: sliceState });
|
|
136
|
+
return slice.selectors[key](fullState, ...params);
|
|
137
|
+
} });
|
|
138
|
+
});
|
|
139
|
+
return acc;
|
|
140
|
+
}, {});
|
|
141
|
+
this._slices = slices;
|
|
142
|
+
this._rootReducer = slices.reduce((acc, slice) => {
|
|
143
|
+
acc[slice.name] = slice.reducer;
|
|
144
|
+
return acc;
|
|
145
|
+
}, {});
|
|
146
|
+
this._store = configureStore({
|
|
147
|
+
reducer: this._rootReducer,
|
|
148
|
+
devTools: isDev(),
|
|
149
|
+
preloadedState,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Creates a Redux store with FAST component integration.
|
|
155
|
+
*
|
|
156
|
+
* @remarks
|
|
157
|
+
* This function creates a Redux store that integrates seamlessly with FAST components.
|
|
158
|
+
* It automatically binds actions and selectors, provides subscription methods, and handles
|
|
159
|
+
* FAST Observable notifications.
|
|
160
|
+
*
|
|
161
|
+
* @param slices - Array of slices to combine into the store
|
|
162
|
+
* @param preloadedState - Initial state that matches the slice structure
|
|
163
|
+
* @returns Object containing store, actions, selectors, and utility functions
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* ```typescript
|
|
167
|
+
* const { store, actions, selectors, subscribeKey } = createStore(
|
|
168
|
+
* [userSlice, cartSlice],
|
|
169
|
+
* {
|
|
170
|
+
* user: { name: '', email: '' },
|
|
171
|
+
* cart: { items: [], total: 0 }
|
|
172
|
+
* }
|
|
173
|
+
* );
|
|
174
|
+
* ```
|
|
175
|
+
*
|
|
176
|
+
* @public
|
|
177
|
+
*/
|
|
178
|
+
export const createStore = (slices, preloadedState) => {
|
|
179
|
+
const observableStore = new ObservableStore(slices, preloadedState);
|
|
180
|
+
const actions = observableStore.getActions();
|
|
181
|
+
const storeProxy = new Proxy({}, {
|
|
182
|
+
get(target, propertyName) {
|
|
183
|
+
return observableStore.getSlice(propertyName);
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
return {
|
|
187
|
+
store: storeProxy,
|
|
188
|
+
actions,
|
|
189
|
+
selectors: observableStore.getSelectors(),
|
|
190
|
+
rootReducer: observableStore.getRootReducer(),
|
|
191
|
+
dispatch: observableStore.dispatch,
|
|
192
|
+
notify: observableStore.notify,
|
|
193
|
+
subscribe: observableStore.subscribe,
|
|
194
|
+
getState: () => observableStore.getReduxStore().getState(),
|
|
195
|
+
subscribeKey: observableStore.subscribeKey,
|
|
196
|
+
};
|
|
197
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|