@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 ADDED
@@ -0,0 +1,185 @@
1
+ # Genesis Foundation Redux
2
+
3
+ [![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lerna.js.org/)
4
+ [![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](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,3 @@
1
+ export * from './store';
2
+ export * from './types';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -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,2 @@
1
+ export * from './store';
2
+ export * from './types';
@@ -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 {};