native-document 1.0.94 → 1.0.98
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/{src/devtools/hrm → devtools}/ComponentRegistry.js +2 -2
- package/devtools/index.js +8 -0
- package/{src/devtools/plugin.js → devtools/plugin/dev-tools-plugin.js} +2 -2
- package/{src/devtools/hrm/nd-vite-hot-reload.js → devtools/transformers/nd-vite-devtools.js} +16 -6
- package/devtools/transformers/src/transformComponentForHrm.js +74 -0
- package/devtools/transformers/src/transformJsFile.js +9 -0
- package/devtools/transformers/src/utils.js +79 -0
- package/devtools/widget/Widget.js +48 -0
- package/devtools/widget/widget.css +81 -0
- package/devtools/widget.js +23 -0
- package/dist/native-document.components.min.js +2441 -1191
- package/dist/native-document.dev.js +2710 -1359
- package/dist/native-document.dev.js.map +1 -1
- package/dist/native-document.devtools.min.js +1 -1
- package/dist/native-document.min.js +1 -1
- package/docs/cache.md +1 -1
- package/docs/core-concepts.md +1 -1
- package/docs/native-document-element.md +51 -15
- package/docs/observables.md +310 -306
- package/docs/state-management.md +198 -193
- package/index.def.js +762 -26
- package/package.json +1 -1
- package/readme.md +1 -1
- package/src/core/data/ObservableChecker.js +2 -0
- package/src/core/data/ObservableItem.js +97 -0
- package/src/core/data/ObservableObject.js +182 -0
- package/src/core/data/Store.js +364 -34
- package/src/core/data/observable-helpers/object.js +2 -166
- package/src/core/elements/anchor.js +28 -20
- package/src/core/elements/control/for-each.js +1 -1
- package/src/core/utils/formatters.js +91 -0
- package/src/core/utils/localstorage.js +57 -0
- package/src/core/utils/validator.js +0 -2
- package/src/core/wrappers/DocumentObserver.js +102 -31
- package/src/core/wrappers/ElementCreator.js +5 -0
- package/src/core/wrappers/NDElement.js +32 -1
- package/src/core/wrappers/prototypes/nd-element.transition.extensions.js +83 -0
- package/src/devtools.js +9 -0
- package/src/fetch/NativeFetch.js +5 -2
- package/types/elements.d.ts +580 -2
- package/types/nd-element.d.ts +6 -0
- package/types/observable.d.ts +71 -15
- package/types/plugins-manager.d.ts +1 -1
- package/types/store.d.ts +33 -6
- package/hrm.js +0 -7
- package/src/devtools/app/App.js +0 -66
- package/src/devtools/app/app.css +0 -0
- package/src/devtools/hrm/transformComponent.js +0 -129
- package/src/devtools/index.js +0 -18
- package/src/devtools/widget/DevToolsWidget.js +0 -26
- /package/{src/devtools/hrm → devtools/transformers/templates}/hrm.hook.template.js +0 -0
- /package/{src/devtools/hrm → devtools/transformers/templates}/hrm.orbservable.hook.template.js +0 -0
package/docs/state-management.md
CHANGED
|
@@ -53,23 +53,158 @@ const cartStore = Store.create('cart', {
|
|
|
53
53
|
});
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
### Store
|
|
56
|
+
### Resettable Store
|
|
57
57
|
|
|
58
|
+
Use `createResettable()` when the store needs to return to its initial value — for example on logout or route change.
|
|
58
59
|
```javascript
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
const userStore = Store.createResettable('user', {
|
|
61
|
+
id: null,
|
|
62
|
+
name: '',
|
|
63
|
+
email: '',
|
|
64
|
+
isLoggedIn: false
|
|
62
65
|
});
|
|
63
66
|
|
|
64
|
-
//
|
|
67
|
+
// Reset to initial value at any time
|
|
68
|
+
Store.reset('user');
|
|
69
|
+
// -> { id: null, name: '', email: '', isLoggedIn: false }
|
|
70
|
+
// -> all subscribers are notified automatically
|
|
71
|
+
|
|
72
|
+
// Standard create() does not support reset
|
|
73
|
+
Store.reset('theme'); // ❌ throws : this store is not resettable
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Store with Computed Values
|
|
77
|
+
|
|
78
|
+
For a computed value that stays local, use `Observable.computed()` :
|
|
79
|
+
```javascript
|
|
65
80
|
const cartTotal = Observable.computed(() => {
|
|
66
|
-
const cart = cartStore
|
|
81
|
+
const cart = cartStore.val();
|
|
67
82
|
const subtotal = cart.items.reduce((sum, item) => sum + (item.price * item.quantity), 0);
|
|
68
|
-
|
|
69
|
-
return subtotal + tax;
|
|
83
|
+
return subtotal + (subtotal * cart.taxRate);
|
|
70
84
|
}, [cartStore]);
|
|
71
85
|
```
|
|
72
86
|
|
|
87
|
+
For a computed value that must be accessible globally via the Store registry, use `createComposed()` :
|
|
88
|
+
```javascript
|
|
89
|
+
Store.create('products', [{ id: 1, price: 10 }]);
|
|
90
|
+
Store.create('cart', [{ productId: 1, quantity: 2 }]);
|
|
91
|
+
|
|
92
|
+
Store.createComposed('total', () => {
|
|
93
|
+
const products = Store.get('products').val();
|
|
94
|
+
const cart = Store.get('cart').val();
|
|
95
|
+
return cart.reduce((sum, item) => {
|
|
96
|
+
const product = products.find(p => p.id === item.productId);
|
|
97
|
+
return sum + (product.price * item.quantity);
|
|
98
|
+
}, 0);
|
|
99
|
+
}, ['products', 'cart']);
|
|
100
|
+
|
|
101
|
+
// Access like any other store — read-only
|
|
102
|
+
const total = Store.follow('total');
|
|
103
|
+
total.val(); // -> 20
|
|
104
|
+
|
|
105
|
+
// Store.use('total') -> ❌ throws : composed stores are read-only
|
|
106
|
+
// Store.reset('total') -> ❌ throws : composed stores cannot be reset
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Persistent Store
|
|
110
|
+
|
|
111
|
+
Use `createPersistent()` when the store needs to survive page reloads. The value is automatically restored from localStorage on load and saved on every change.
|
|
112
|
+
```javascript
|
|
113
|
+
const theme = Store.createPersistent('theme', 'light');
|
|
114
|
+
theme.set('dark'); // saved to localStorage automatically
|
|
115
|
+
|
|
116
|
+
// On next page load — restored automatically
|
|
117
|
+
Store.get('theme').val(); // "dark"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Use `createPersistentResettable()` when you need both persistence and reset capability. Reset clears the localStorage entry and restores the default value.
|
|
121
|
+
```javascript
|
|
122
|
+
const session = Store.createPersistentResettable('session', { id: null, name: '' });
|
|
123
|
+
session.set({ id: 1, name: 'John' }); // saved
|
|
124
|
+
|
|
125
|
+
Store.reset('session');
|
|
126
|
+
// -> { id: null, name: '' }
|
|
127
|
+
// -> localStorage entry removed
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Both methods accept an optional custom localStorage key — useful when the store name conflicts with existing keys:
|
|
131
|
+
```javascript
|
|
132
|
+
Store.createPersistent('theme', 'light', 'app:theme');
|
|
133
|
+
Store.createPersistentResettable('session', null, 'app:session');
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Store Groups
|
|
137
|
+
|
|
138
|
+
Use `Store.group()` to create an isolated store namespace. Each group is a fully independent `StoreFactory` instance — no key conflicts, no shared state with the parent store.
|
|
139
|
+
```javascript
|
|
140
|
+
const EventStore = Store.group('events', (group) => {
|
|
141
|
+
group.create('catalog', []);
|
|
142
|
+
group.create('filters', { category: null, date: null, city: null });
|
|
143
|
+
|
|
144
|
+
group.createResettable('selected', null);
|
|
145
|
+
|
|
146
|
+
group.createComposed('filtered', () => {
|
|
147
|
+
const catalog = EventStore.get('catalog').val();
|
|
148
|
+
const filters = EventStore.get('filters').val();
|
|
149
|
+
return catalog.filter(event => {
|
|
150
|
+
if (filters.category && event.category !== filters.category) return false;
|
|
151
|
+
if (filters.city && event.city !== filters.city) return false;
|
|
152
|
+
return true;
|
|
153
|
+
});
|
|
154
|
+
}, ['catalog', 'filters']);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Usage — same API as Store
|
|
158
|
+
EventStore.use('catalog'); // two-way follower
|
|
159
|
+
EventStore.follow('filtered'); // read-only follower
|
|
160
|
+
EventStore.get('filters'); // raw observable
|
|
161
|
+
|
|
162
|
+
// Direct property access — raw observable, always read-only
|
|
163
|
+
EventStore.catalog;
|
|
164
|
+
EventStore.filters;
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Groups can reference each other in `createComposed()` for cross-group derived state:
|
|
168
|
+
```javascript
|
|
169
|
+
const CartStore = Store.group('cart', (group) => {
|
|
170
|
+
group.create('items', []);
|
|
171
|
+
|
|
172
|
+
group.createComposed('total', () => {
|
|
173
|
+
return CartStore.get('items').val()
|
|
174
|
+
.reduce((sum, item) => sum + item.price * item.qty, 0);
|
|
175
|
+
}, ['items']);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
const OrderStore = Store.group('orders', (group) => {
|
|
179
|
+
group.createComposed('summary', () => {
|
|
180
|
+
const items = CartStore.get('items').val();
|
|
181
|
+
const events = EventStore.get('catalog').val();
|
|
182
|
+
return { items, events };
|
|
183
|
+
}, [CartStore.get('items'), EventStore.get('catalog')]);
|
|
184
|
+
});
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Groups can also be created without a name:
|
|
188
|
+
```javascript
|
|
189
|
+
const CartStore = Store.group((group) => {
|
|
190
|
+
group.create('items', []);
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Use Groups to Organize Domain State
|
|
195
|
+
```javascript
|
|
196
|
+
// Good: domain-driven grouping
|
|
197
|
+
const UserStore = Store.group('user', (g) => { g.create('session', null); });
|
|
198
|
+
const EventStore = Store.group('events', (g) => { g.create('catalog', []); });
|
|
199
|
+
const CartStore = Store.group('cart', (g) => { g.create('items', []); });
|
|
200
|
+
|
|
201
|
+
// Avoid: flat global stores for everything
|
|
202
|
+
Store.create('userSession', null);
|
|
203
|
+
Store.create('eventCatalog', []);
|
|
204
|
+
Store.create('cartItems', []);
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
|
|
73
208
|
## Using Stores
|
|
74
209
|
|
|
75
210
|
Access and react to store changes using Store.use() or Store.follow():
|
|
@@ -82,8 +217,8 @@ const UserProfile = () => {
|
|
|
82
217
|
const user = Store.use('user');
|
|
83
218
|
|
|
84
219
|
return Div([
|
|
85
|
-
H1(['User Profile: ', user.
|
|
86
|
-
P(['Email: ', user.
|
|
220
|
+
H1(['User Profile: ', user.name]),
|
|
221
|
+
P(['Email: ', user.email]),
|
|
87
222
|
P(['Status: ', user.check(u => u.isLoggedIn ? 'Online' : 'Offline')])
|
|
88
223
|
]);
|
|
89
224
|
};
|
|
@@ -103,12 +238,15 @@ const UserMenu = () => {
|
|
|
103
238
|
};
|
|
104
239
|
```
|
|
105
240
|
|
|
106
|
-
### Store.follow() -
|
|
241
|
+
### Store.follow() - Read-only Access
|
|
107
242
|
|
|
108
243
|
```javascript
|
|
109
244
|
const NotificationBadge = () => {
|
|
110
|
-
// Follow
|
|
245
|
+
// Follow returns a read-only reference — any attempt to call
|
|
246
|
+
// .set(), .toggle() or .reset() will throw a NativeDocumentError.
|
|
247
|
+
// Use this when a component should only read from the store.
|
|
111
248
|
const notifications = Store.follow('notifications');
|
|
249
|
+
// notifications.set(...) -> ❌ throws NativeDocumentError
|
|
112
250
|
|
|
113
251
|
return ShowIf(notifications.check(list => list.length > 0),
|
|
114
252
|
() => Span({ class: 'badge' }, notifications.$value.length)
|
|
@@ -116,6 +254,38 @@ const NotificationBadge = () => {
|
|
|
116
254
|
};
|
|
117
255
|
```
|
|
118
256
|
|
|
257
|
+
### Store.get() - Raw Access
|
|
258
|
+
|
|
259
|
+
Returns the raw store observable directly — no follower, no cleanup contract. Use this for direct read access when you don't need to unsubscribe.
|
|
260
|
+
|
|
261
|
+
> **Warning:** mutations on this observer impact all subscribers immediately.
|
|
262
|
+
```javascript
|
|
263
|
+
const userStore = Store.get('user');
|
|
264
|
+
|
|
265
|
+
if (userStore.$value.isLoggedIn) {
|
|
266
|
+
console.log('User is logged in');
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
userStore.subscribe(newUser => {
|
|
270
|
+
console.log('User changed:', newUser);
|
|
271
|
+
});
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Direct Property Access
|
|
275
|
+
|
|
276
|
+
Stores and groups expose their observables as direct properties via a Proxy. Property access returns the raw observable — equivalent to calling `Store.get()`. Any attempt to assign or delete a property will throw.
|
|
277
|
+
```javascript
|
|
278
|
+
// Equivalent to Store.get('catalog')
|
|
279
|
+
EventStore.catalog;
|
|
280
|
+
|
|
281
|
+
// For a read-only follower, use follow() explicitly
|
|
282
|
+
EventStore.follow('catalog');
|
|
283
|
+
|
|
284
|
+
// Direct assignment is forbidden
|
|
285
|
+
EventStore.catalog = []; // ❌ throws : Store structure is immutable
|
|
286
|
+
delete EventStore.catalog; // ❌ throws : Store keys cannot be deleted
|
|
287
|
+
```
|
|
288
|
+
|
|
119
289
|
## Updating Store State
|
|
120
290
|
|
|
121
291
|
Modify store state using the returned observable's methods:
|
|
@@ -147,7 +317,7 @@ const LoginForm = () => {
|
|
|
147
317
|
...user.$value,
|
|
148
318
|
email: email.$value,
|
|
149
319
|
isLoggedIn: true,
|
|
150
|
-
name: 'User Name' //
|
|
320
|
+
name: 'User Name' // ...
|
|
151
321
|
});
|
|
152
322
|
};
|
|
153
323
|
|
|
@@ -215,181 +385,18 @@ userStore.subscribe(newUser => {
|
|
|
215
385
|
});
|
|
216
386
|
```
|
|
217
387
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
## Store Cleanup
|
|
221
|
-
|
|
222
|
-
Properly clean up stores when they're no longer needed:
|
|
223
|
-
|
|
224
|
-
### Manual Cleanup
|
|
225
|
-
|
|
226
|
-
```javascript
|
|
227
|
-
// Remove store and all its subscribers
|
|
228
|
-
Store.delete('temporaryStore');
|
|
229
|
-
|
|
230
|
-
// Clean up specific subscriber
|
|
231
|
-
const userStore = Store.use('user');
|
|
232
|
-
userStore.destroy(); // Clean up this subscriber instance
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
## Performance Considerations
|
|
236
|
-
|
|
237
|
-
### Efficient Store Updates
|
|
238
|
-
|
|
239
|
-
```javascript
|
|
240
|
-
// Good: Batch related updates
|
|
241
|
-
const updateUserProfile = (name, email, preferences) => {
|
|
242
|
-
const user = Store.use('user');
|
|
243
|
-
user.set({
|
|
244
|
-
...user.$value,
|
|
245
|
-
name,
|
|
246
|
-
email,
|
|
247
|
-
preferences: {
|
|
248
|
-
...user.$value.preferences,
|
|
249
|
-
...preferences
|
|
250
|
-
}
|
|
251
|
-
});
|
|
252
|
-
};
|
|
253
|
-
|
|
254
|
-
// Less efficient: Multiple separate updates
|
|
255
|
-
const updateUserProfileSlow = (name, email, preferences) => {
|
|
256
|
-
const user = Store.use('user');
|
|
257
|
-
user.set({ ...user.$value, name }); // Triggers update
|
|
258
|
-
user.set({ ...user.$value, email }); // Triggers update
|
|
259
|
-
user.set({ ...user.$value, preferences }); // Triggers update
|
|
260
|
-
};
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
### Selective Subscriptions
|
|
264
|
-
|
|
265
|
-
```javascript
|
|
266
|
-
// Subscribe to specific parts of store
|
|
267
|
-
const UserName = () => {
|
|
268
|
-
const user = Store.use('user');
|
|
269
|
-
|
|
270
|
-
// Only re-render when name changes
|
|
271
|
-
const userName = user.check(u => u.name);
|
|
272
|
-
|
|
273
|
-
return Span(userName);
|
|
274
|
-
};
|
|
275
|
-
|
|
276
|
-
// More efficient than re-rendering entire user profile
|
|
277
|
-
// when only name is needed
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
## Best Practices
|
|
281
|
-
|
|
282
|
-
### 1. Use Descriptive Store Names
|
|
283
|
-
|
|
284
|
-
```javascript
|
|
285
|
-
// Good: Clear, descriptive names
|
|
286
|
-
const userAuthStore = Store.create('userAuth', defaultUser);
|
|
287
|
-
const shoppingCartStore = Store.create('shoppingCart', defaultCart);
|
|
288
|
-
const appSettingsStore = Store.create('appSettings', defaultSettings);
|
|
289
|
-
|
|
290
|
-
// Less clear: Generic names
|
|
291
|
-
const store1 = Store.create('data', {});
|
|
292
|
-
const state = Store.create('state', {});
|
|
293
|
-
```
|
|
294
|
-
|
|
295
|
-
### 2. Initialize with Complete Data Structures
|
|
296
|
-
|
|
297
|
-
```javascript
|
|
298
|
-
// Good: Complete initial structure
|
|
299
|
-
const userStore = Store.create('user', {
|
|
300
|
-
id: null,
|
|
301
|
-
name: '',
|
|
302
|
-
email: '',
|
|
303
|
-
preferences: {
|
|
304
|
-
theme: 'light',
|
|
305
|
-
notifications: true
|
|
306
|
-
},
|
|
307
|
-
isLoggedIn: false
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
// Problematic: Incomplete structure
|
|
311
|
-
const userStore = Store.create('user', {});
|
|
312
|
-
// Later code might fail when accessing user.preferences.theme
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
### 3. Create Service Objects for Complex Operations
|
|
316
|
-
|
|
317
|
-
```javascript
|
|
318
|
-
// Good: Organized actions
|
|
319
|
-
const AuthService= (function() {
|
|
320
|
-
const authUser = Store.create('user', {});
|
|
321
|
-
|
|
322
|
-
return {
|
|
323
|
-
login: (credentials) => { /* ... */ },
|
|
324
|
-
logout: () => { /* ... */ },
|
|
325
|
-
updateProfile: (data) => { /* ... */ },
|
|
326
|
-
updatePreferences: (prefs) => { /* ... */ }
|
|
327
|
-
};
|
|
328
|
-
}());
|
|
329
|
-
|
|
330
|
-
// Instead of scattered update logic throughout components
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
### 4. Use Store for Cross-Component State Only
|
|
334
|
-
|
|
335
|
-
```javascript
|
|
336
|
-
// Good: Local state for component-specific data
|
|
337
|
-
const ContactForm = () => {
|
|
338
|
-
const name = Observable(''); // Local state
|
|
339
|
-
const email = Observable(''); // Local state
|
|
340
|
-
const user = Store.use('user'); // Global state
|
|
341
|
-
|
|
342
|
-
return Form([/* ... */]);
|
|
343
|
-
};
|
|
344
|
-
|
|
345
|
-
// Avoid: Putting everything in stores
|
|
346
|
-
// Don't store form input state globally unless shared
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
## Debugging Stores
|
|
350
|
-
|
|
351
|
-
### Store State Inspection
|
|
352
|
-
|
|
353
|
-
```javascript
|
|
354
|
-
// Log current store state
|
|
355
|
-
console.log('Current user:', Store.get('user').$value);
|
|
356
|
-
|
|
357
|
-
// Monitor store changes
|
|
358
|
-
Store.get('user').subscribe(newUser => {
|
|
359
|
-
console.log('User changed:', newUser);
|
|
360
|
-
});
|
|
361
|
-
|
|
362
|
-
// Check all followers of a store
|
|
363
|
-
const userData = Store.getWithSubscribers('user');
|
|
364
|
-
console.log('Store value:', userData.observer.$value);
|
|
365
|
-
console.log('Followers count:', userData.subscribers.size);
|
|
366
|
-
```
|
|
367
|
-
|
|
368
|
-
## Common Patterns
|
|
369
|
-
|
|
370
|
-
### Persistent Store
|
|
371
|
-
|
|
388
|
+
### Checking Store Existence
|
|
372
389
|
```javascript
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
const
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
const store = Store.create(name, initialValue);
|
|
379
|
-
|
|
380
|
-
// Save changes to localStorage
|
|
381
|
-
store.subscribe(newValue => {
|
|
382
|
-
localStorage.setItem(storageKey, JSON.stringify(newValue));
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
return store;
|
|
386
|
-
};
|
|
390
|
+
// Check if a store exists before accessing it
|
|
391
|
+
if (Store.has('cart')) {
|
|
392
|
+
const cart = Store.use('cart');
|
|
393
|
+
// ...
|
|
394
|
+
}
|
|
387
395
|
|
|
388
|
-
//
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
}, 'app_preferences');
|
|
396
|
+
// Typical use case : dynamic module initialization
|
|
397
|
+
if (!Store.has('cart')) {
|
|
398
|
+
Store.create('cart', { items: [], total: 0 });
|
|
399
|
+
}
|
|
393
400
|
```
|
|
394
401
|
|
|
395
402
|
### Store Composition
|
|
@@ -402,13 +409,11 @@ const createAppState = () => {
|
|
|
402
409
|
const settings = Store.create('settings', defaultSettings);
|
|
403
410
|
|
|
404
411
|
// Computed store that combines others
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
};
|
|
411
|
-
}, [auth, cart, settings]);
|
|
412
|
+
Store.createComposed('appStatus', () => ({
|
|
413
|
+
isLoggedIn: Store.get('auth').val().user !== null,
|
|
414
|
+
cartItems: Store.get('cart').val().items.length,
|
|
415
|
+
theme: Store.get('settings').val().theme
|
|
416
|
+
}), ['auth', 'cart', 'settings']);
|
|
412
417
|
|
|
413
418
|
return { auth, cart, settings, appStatus };
|
|
414
419
|
};
|