@miurajs/miura-data-flow 0.0.1
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 +565 -0
- package/index.ts +1 -0
- package/package.json +30 -0
- package/src/global-state.ts +158 -0
- package/src/middleware.ts +162 -0
- package/src/miura-data-flow.ts +114 -0
- package/src/providers/firebase-provider.ts +39 -0
- package/src/providers/graphql-provider.ts +39 -0
- package/src/providers/grpc-web-provider.ts +35 -0
- package/src/providers/index.ts +11 -0
- package/src/providers/indexed-db-provider.ts +48 -0
- package/src/providers/local-storage-provider.ts +36 -0
- package/src/providers/provider-manager.ts +21 -0
- package/src/providers/provider.ts +23 -0
- package/src/providers/rest-provider.ts +351 -0
- package/src/providers/s3-provider.ts +41 -0
- package/src/providers/supabase-provider.ts +45 -0
- package/src/providers/websockets-provider.ts +54 -0
- package/src/store.ts +237 -0
- package/stories/data-flow-demo.stories.ts +640 -0
- package/tsconfig.json +17 -0
package/README.md
ADDED
|
@@ -0,0 +1,565 @@
|
|
|
1
|
+
# miura Data Flow
|
|
2
|
+
|
|
3
|
+
Modern, reactive state management for miura applications. Combines the best of Redux, Zustand, and modern patterns with zero boilerplate and maximum performance.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **🚀 Reactive State Management** - Automatic updates when state changes
|
|
8
|
+
- **🔧 Middleware System** - Extensible with logging, persistence, API integration
|
|
9
|
+
- **🌍 Global State** - Application-wide state management
|
|
10
|
+
- **💾 Built-in Caching** - Intelligent caching with TTL
|
|
11
|
+
- **🔌 API Integration** - Automatic API calls with state updates
|
|
12
|
+
- **🛠️ DevTools Support** - Redux DevTools integration
|
|
13
|
+
- **📝 TypeScript First** - Full type safety and IntelliSense
|
|
14
|
+
- **⚡ Performance Optimized** - Efficient subscriptions and updates
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { Store, globalState, createLoggerMiddleware } from '@miura/miura-data-flow';
|
|
20
|
+
|
|
21
|
+
// Create a store
|
|
22
|
+
const store = new Store({ count: 0, user: null });
|
|
23
|
+
|
|
24
|
+
// Add middleware
|
|
25
|
+
store.use(createLoggerMiddleware());
|
|
26
|
+
|
|
27
|
+
// Define actions
|
|
28
|
+
store.defineActions({
|
|
29
|
+
increment: (state) => ({ count: state.count + 1 }),
|
|
30
|
+
setUser: (state, user) => ({ user })
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Subscribe to changes
|
|
34
|
+
const unsubscribe = store.subscribe((state, prevState) => {
|
|
35
|
+
console.log('State changed:', state);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Dispatch actions
|
|
39
|
+
await store.dispatch('increment');
|
|
40
|
+
await store.dispatch('setUser', { id: '1', name: 'John' });
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Core Concepts
|
|
44
|
+
|
|
45
|
+
### Store
|
|
46
|
+
|
|
47
|
+
The main state container that manages your application state.
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { Store } from '@miura/miura-data-flow';
|
|
51
|
+
|
|
52
|
+
interface AppState {
|
|
53
|
+
count: number;
|
|
54
|
+
user: User | null;
|
|
55
|
+
todos: Todo[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const store = new Store<AppState>({
|
|
59
|
+
count: 0,
|
|
60
|
+
user: null,
|
|
61
|
+
todos: []
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Actions
|
|
66
|
+
|
|
67
|
+
Define how your state can be updated.
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
store.defineActions({
|
|
71
|
+
// Simple state update
|
|
72
|
+
increment: (state) => ({ count: state.count + 1 }),
|
|
73
|
+
|
|
74
|
+
// With parameters
|
|
75
|
+
setUser: (state, user: User) => ({ user }),
|
|
76
|
+
|
|
77
|
+
// Async actions
|
|
78
|
+
async fetchUser: async (state, userId: string) => {
|
|
79
|
+
const user = await fetch(`/api/users/${userId}`).then(r => r.json());
|
|
80
|
+
return { user };
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
// Complex updates
|
|
84
|
+
addTodo: (state, todo: Todo) => ({
|
|
85
|
+
todos: [...state.todos, todo]
|
|
86
|
+
})
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Subscriptions
|
|
91
|
+
|
|
92
|
+
React to state changes automatically.
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
// Subscribe to all changes
|
|
96
|
+
const unsubscribe = store.subscribe((state, prevState) => {
|
|
97
|
+
console.log('State changed:', state);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Subscribe to specific property
|
|
101
|
+
const unsubscribeCount = store.subscribeTo('count', (count, prevCount) => {
|
|
102
|
+
console.log(`Count changed from ${prevCount} to ${count}`);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Subscribe with selector
|
|
106
|
+
const unsubscribeUser = store.subscribe(
|
|
107
|
+
(state, prevState) => {
|
|
108
|
+
console.log('User changed:', state.user);
|
|
109
|
+
},
|
|
110
|
+
(state) => state.user // Only trigger when user changes
|
|
111
|
+
);
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Middleware System
|
|
115
|
+
|
|
116
|
+
### Built-in Middleware
|
|
117
|
+
|
|
118
|
+
#### Logger Middleware
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
import { createLoggerMiddleware } from '@miura/miura-data-flow';
|
|
122
|
+
|
|
123
|
+
store.use(createLoggerMiddleware());
|
|
124
|
+
// Logs all actions and state changes to console
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
#### Persistence Middleware
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import { createPersistenceMiddleware } from '@miura/miura-data-flow';
|
|
131
|
+
|
|
132
|
+
store.use(createPersistenceMiddleware(['user', 'settings']));
|
|
133
|
+
// Automatically saves/loads specified properties to localStorage
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
#### API Middleware
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
import { createApiMiddleware } from '@miura/miura-data-flow';
|
|
140
|
+
|
|
141
|
+
store.use(createApiMiddleware({
|
|
142
|
+
baseURL: 'https://api.example.com',
|
|
143
|
+
headers: {
|
|
144
|
+
'Authorization': 'Bearer token'
|
|
145
|
+
},
|
|
146
|
+
timeout: 5000
|
|
147
|
+
}));
|
|
148
|
+
|
|
149
|
+
// Now you can dispatch API actions
|
|
150
|
+
store.defineActions({
|
|
151
|
+
api_fetchUsers: () => {}, // Will automatically fetch /fetchUsers
|
|
152
|
+
api_createUser: () => {} // Will automatically POST to /createUser
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
await store.dispatch('api_fetchUsers');
|
|
156
|
+
await store.dispatch('api_createUser', { method: 'POST', data: userData });
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
#### Cache Middleware
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
import { createCacheMiddleware } from '@miura/miura-data-flow';
|
|
163
|
+
|
|
164
|
+
store.use(createCacheMiddleware(5 * 60 * 1000)); // 5 minutes TTL
|
|
165
|
+
// Caches API responses for 5 minutes
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
#### DevTools Middleware
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
import { createDevToolsMiddleware } from '@miura/miura-data-flow';
|
|
172
|
+
|
|
173
|
+
store.use(createDevToolsMiddleware('MyApp'));
|
|
174
|
+
// Enables Redux DevTools integration
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Redux DevTools Integration
|
|
178
|
+
|
|
179
|
+
Redux DevTools is a powerful browser extension that provides real-time debugging for state management. Even though miura Data Flow isn't Redux, we integrate with it because it's the industry standard for state debugging.
|
|
180
|
+
|
|
181
|
+
#### What Redux DevTools Provides:
|
|
182
|
+
|
|
183
|
+
- **🔍 State Inspector** - Real-time visualization of your entire state tree
|
|
184
|
+
- **📜 Action Monitor** - Complete log of all dispatched actions with timing
|
|
185
|
+
- **⏰ Time-travel Debugging** - Jump back to any previous state
|
|
186
|
+
- **📊 Diff View** - Before/after comparison with highlighted changes
|
|
187
|
+
- **⚡ Performance Metrics** - Action timing and state size monitoring
|
|
188
|
+
|
|
189
|
+
#### Setup Instructions:
|
|
190
|
+
|
|
191
|
+
1. **Install the Browser Extension:**
|
|
192
|
+
- **Chrome**: [Redux DevTools Extension](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd)
|
|
193
|
+
- **Firefox**: [Redux DevTools Extension](https://addons.mozilla.org/en-US/firefox/addon/reduxdevtools/)
|
|
194
|
+
|
|
195
|
+
2. **Enable in Your Code:**
|
|
196
|
+
```typescript
|
|
197
|
+
import { createDevToolsMiddleware } from '@miura/miura-data-flow';
|
|
198
|
+
|
|
199
|
+
store.use(createDevToolsMiddleware('MyApp'));
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
3. **Open DevTools:**
|
|
203
|
+
- Press `F12` → Click the **Redux** tab
|
|
204
|
+
- Or right-click → **Inspect** → **Redux** tab
|
|
205
|
+
|
|
206
|
+
#### What You'll See:
|
|
207
|
+
|
|
208
|
+
When you dispatch actions, they appear in DevTools like this:
|
|
209
|
+
|
|
210
|
+
```
|
|
211
|
+
Action: increment
|
|
212
|
+
Payload: []
|
|
213
|
+
State Before: { count: 0, user: null }
|
|
214
|
+
State After: { count: 1, user: null }
|
|
215
|
+
|
|
216
|
+
Action: setUser
|
|
217
|
+
Payload: [{ id: '1', name: 'John' }]
|
|
218
|
+
State Before: { count: 1, user: null }
|
|
219
|
+
State After: { count: 1, user: { id: '1', name: 'John' } }
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
#### Complete Example:
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
import { Store, createDevToolsMiddleware } from '@miura/miura-data-flow';
|
|
226
|
+
|
|
227
|
+
// Create store
|
|
228
|
+
const store = new Store({
|
|
229
|
+
user: null,
|
|
230
|
+
todos: [],
|
|
231
|
+
theme: 'light'
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// Enable DevTools integration
|
|
235
|
+
store.use(createDevToolsMiddleware('TodoApp'));
|
|
236
|
+
|
|
237
|
+
// Define actions
|
|
238
|
+
store.defineActions({
|
|
239
|
+
login: (state, user) => ({ user }),
|
|
240
|
+
addTodo: (state, todo) => ({ todos: [...state.todos, todo] }),
|
|
241
|
+
toggleTheme: (state) => ({ theme: state.theme === 'light' ? 'dark' : 'light' })
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// These actions will now be visible in Redux DevTools:
|
|
245
|
+
await store.dispatch('login', { id: '1', name: 'John' });
|
|
246
|
+
await store.dispatch('addTodo', { id: '1', text: 'Buy milk' });
|
|
247
|
+
await store.dispatch('toggleTheme');
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
#### DevTools Features:
|
|
251
|
+
|
|
252
|
+
**State Inspector:**
|
|
253
|
+
- View your entire state tree in real-time
|
|
254
|
+
- Expand/collapse nested objects
|
|
255
|
+
- Search through state properties
|
|
256
|
+
|
|
257
|
+
**Action Monitor:**
|
|
258
|
+
- See all dispatched actions with timestamps
|
|
259
|
+
- Inspect action payloads and arguments
|
|
260
|
+
- Monitor action execution time
|
|
261
|
+
|
|
262
|
+
**Time-travel Debugging:**
|
|
263
|
+
- Jump to any previous state
|
|
264
|
+
- Replay actions step by step
|
|
265
|
+
- Compare states at different points in time
|
|
266
|
+
|
|
267
|
+
**Performance Monitoring:**
|
|
268
|
+
- Track action execution time
|
|
269
|
+
- Monitor state size changes
|
|
270
|
+
- Identify performance bottlenecks
|
|
271
|
+
|
|
272
|
+
#### Why Use Redux DevTools?
|
|
273
|
+
|
|
274
|
+
Even though we're not using Redux, Redux DevTools has become the **de facto standard** for state debugging because:
|
|
275
|
+
|
|
276
|
+
- **Universal Adoption** - Most developers are familiar with it
|
|
277
|
+
- **Rich Features** - Time-travel, diff view, performance metrics
|
|
278
|
+
- **Browser Integration** - No additional setup required
|
|
279
|
+
- **Community Support** - Well-maintained and documented
|
|
280
|
+
|
|
281
|
+
It's like having a **super-powered console.log** that shows you exactly what's happening with your state in real-time! 🚀
|
|
282
|
+
|
|
283
|
+
### Debug Information
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
// Get debug info about your store
|
|
287
|
+
console.log(store.getDebugInfo());
|
|
288
|
+
console.log(globalState.getDebugInfo());
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## API Reference
|
|
292
|
+
|
|
293
|
+
### Store
|
|
294
|
+
|
|
295
|
+
- `new Store<T>(initialState: T)` - Create a new store
|
|
296
|
+
- `store.getState(): T` - Get current state
|
|
297
|
+
- `store.get<K>(key: K): T[K]` - Get specific property
|
|
298
|
+
- `store.setState(updater)` - Update state
|
|
299
|
+
- `store.defineActions(actions)` - Define actions
|
|
300
|
+
- `store.dispatch(action, ...args)` - Execute action
|
|
301
|
+
- `store.subscribe(callback, selector?)` - Subscribe to changes
|
|
302
|
+
- `store.subscribeTo(key, callback)` - Subscribe to specific property
|
|
303
|
+
- `store.use(middleware)` - Add middleware
|
|
304
|
+
|
|
305
|
+
### Global State
|
|
306
|
+
|
|
307
|
+
- `globalState.get(key)` - Get global property
|
|
308
|
+
- `globalState.set(key, value)` - Set global property
|
|
309
|
+
- `globalState.subscribe(componentId, properties, callback)` - Subscribe
|
|
310
|
+
- `globalState.subscribeTo(componentId, key, callback)` - Subscribe to property
|
|
311
|
+
- `globalState.dispatch(action, ...args)` - Dispatch global action
|
|
312
|
+
|
|
313
|
+
### Middleware
|
|
314
|
+
|
|
315
|
+
- `createLoggerMiddleware()` - Console logging
|
|
316
|
+
- `createPersistenceMiddleware(keys, storageKey)` - localStorage persistence
|
|
317
|
+
- `createApiMiddleware(config)` - API integration
|
|
318
|
+
- `createCacheMiddleware(ttl)` - Response caching
|
|
319
|
+
- `createDevToolsMiddleware(name)` - Redux DevTools
|
|
320
|
+
|
|
321
|
+
## Best Practices
|
|
322
|
+
|
|
323
|
+
1. **Use TypeScript** - Define interfaces for your state
|
|
324
|
+
2. **Subscribe Selectively** - Only subscribe to what you need
|
|
325
|
+
3. **Unsubscribe Always** - Prevent memory leaks
|
|
326
|
+
4. **Use Actions** - Don't mutate state directly
|
|
327
|
+
5. **Middleware for Side Effects** - Keep actions pure
|
|
328
|
+
6. **Global State Sparingly** - Use for truly global data
|
|
329
|
+
7. **Batch Updates** - Group related state changes
|
|
330
|
+
|
|
331
|
+
## Examples
|
|
332
|
+
|
|
333
|
+
See the [examples directory](./examples) for complete working examples.
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
**miura Data Flow** - Modern state management that just works! 🚀
|
|
338
|
+
|
|
339
|
+
### Custom Middleware
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
import { StoreMiddleware } from '@miura/miura-data-flow';
|
|
343
|
+
|
|
344
|
+
const analyticsMiddleware: StoreMiddleware = {
|
|
345
|
+
name: 'analytics',
|
|
346
|
+
before: (action, args, state) => {
|
|
347
|
+
// Track action before execution
|
|
348
|
+
analytics.track('action_started', { action, args });
|
|
349
|
+
},
|
|
350
|
+
after: (action, args, state, result) => {
|
|
351
|
+
// Track action completion
|
|
352
|
+
analytics.track('action_completed', { action, result });
|
|
353
|
+
},
|
|
354
|
+
error: (action, args, error) => {
|
|
355
|
+
// Track errors
|
|
356
|
+
analytics.track('action_error', { action, error });
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
store.use(analyticsMiddleware);
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
## Global State Management
|
|
364
|
+
|
|
365
|
+
### Using Global State
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
import { globalState } from '@miura/miura-data-flow';
|
|
369
|
+
|
|
370
|
+
// Set global properties
|
|
371
|
+
globalState.set('theme', 'dark');
|
|
372
|
+
globalState.set('user', { id: '1', name: 'John' });
|
|
373
|
+
|
|
374
|
+
// Get global properties
|
|
375
|
+
const theme = globalState.get('theme');
|
|
376
|
+
const user = globalState.get('user');
|
|
377
|
+
|
|
378
|
+
// Subscribe to global state changes
|
|
379
|
+
const unsubscribe = globalState.subscribeTo('my-component', 'theme', (theme) => {
|
|
380
|
+
console.log('Theme changed:', theme);
|
|
381
|
+
});
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
### Global State in Components
|
|
385
|
+
|
|
386
|
+
```typescript
|
|
387
|
+
import { MiuraElement, html } from '@miura/miura-element';
|
|
388
|
+
import { globalState } from '@miura/miura-data-flow';
|
|
389
|
+
|
|
390
|
+
class ThemeToggle extends MiuraElement {
|
|
391
|
+
static properties = {
|
|
392
|
+
theme: { type: String, default: 'light' }
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
connectedCallback() {
|
|
396
|
+
super.connectedCallback();
|
|
397
|
+
|
|
398
|
+
// Subscribe to global theme changes
|
|
399
|
+
this.unsubscribeTheme = globalState.subscribeTo(
|
|
400
|
+
this.tagName,
|
|
401
|
+
'theme',
|
|
402
|
+
(theme) => {
|
|
403
|
+
this.theme = theme;
|
|
404
|
+
this.requestUpdate();
|
|
405
|
+
}
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
disconnectedCallback() {
|
|
410
|
+
super.disconnectedCallback();
|
|
411
|
+
this.unsubscribeTheme?.();
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
toggleTheme() {
|
|
415
|
+
const newTheme = this.theme === 'light' ? 'dark' : 'light';
|
|
416
|
+
globalState.set('theme', newTheme);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
render() {
|
|
420
|
+
return html`
|
|
421
|
+
<button @click=${this.toggleTheme}>
|
|
422
|
+
Current theme: ${this.theme}
|
|
423
|
+
</button>
|
|
424
|
+
`;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
## Integration with miura Framework
|
|
430
|
+
|
|
431
|
+
### Framework Setup
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
import { Store, createApiMiddleware, createCacheMiddleware } from '@miura/miura-data-flow';
|
|
435
|
+
|
|
436
|
+
class miuraFramework {
|
|
437
|
+
private store: Store;
|
|
438
|
+
|
|
439
|
+
constructor() {
|
|
440
|
+
// Initialize store with app state
|
|
441
|
+
this.store = new Store({
|
|
442
|
+
user: null,
|
|
443
|
+
settings: {},
|
|
444
|
+
notifications: []
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
// Add middleware for API and caching
|
|
448
|
+
this.store.use(createApiMiddleware({
|
|
449
|
+
baseURL: process.env.API_URL,
|
|
450
|
+
headers: {
|
|
451
|
+
'Content-Type': 'application/json'
|
|
452
|
+
}
|
|
453
|
+
}));
|
|
454
|
+
|
|
455
|
+
this.store.use(createCacheMiddleware());
|
|
456
|
+
|
|
457
|
+
// Define global actions
|
|
458
|
+
this.store.defineActions({
|
|
459
|
+
setUser: (state, user) => ({ user }),
|
|
460
|
+
addNotification: (state, notification) => ({
|
|
461
|
+
notifications: [...state.notifications, notification]
|
|
462
|
+
})
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Expose store to components
|
|
467
|
+
getStore() {
|
|
468
|
+
return this.store;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
### Component Integration
|
|
474
|
+
|
|
475
|
+
```typescript
|
|
476
|
+
class MyComponent extends MiuraElement {
|
|
477
|
+
// Define global properties this component uses
|
|
478
|
+
global() {
|
|
479
|
+
return {
|
|
480
|
+
user: null,
|
|
481
|
+
theme: 'light'
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
connected() {
|
|
486
|
+
// Subscribe to global state
|
|
487
|
+
this.unsubscribeUser = globalState.subscribeTo(
|
|
488
|
+
this.tagName,
|
|
489
|
+
'user',
|
|
490
|
+
(user) => this.handleUserChange(user)
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
disconnected() {
|
|
495
|
+
this.unsubscribeUser?.();
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
handleUserChange(user) {
|
|
499
|
+
// React to global user changes
|
|
500
|
+
this.requestUpdate();
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
## Performance Optimization
|
|
506
|
+
|
|
507
|
+
### Selective Subscriptions
|
|
508
|
+
|
|
509
|
+
```typescript
|
|
510
|
+
// Only subscribe to what you need
|
|
511
|
+
const unsubscribe = store.subscribe(
|
|
512
|
+
(state, prevState) => {
|
|
513
|
+
// Only trigger when user.todos changes
|
|
514
|
+
},
|
|
515
|
+
(state) => state.user?.todos
|
|
516
|
+
);
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
### Batch Updates
|
|
520
|
+
|
|
521
|
+
```typescript
|
|
522
|
+
// Multiple updates in one action
|
|
523
|
+
store.defineActions({
|
|
524
|
+
updateUserProfile: (state, updates) => ({
|
|
525
|
+
user: { ...state.user, ...updates },
|
|
526
|
+
lastUpdated: Date.now()
|
|
527
|
+
})
|
|
528
|
+
});
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
### Memory Management
|
|
532
|
+
|
|
533
|
+
```typescript
|
|
534
|
+
// Always unsubscribe to prevent memory leaks
|
|
535
|
+
class MyComponent {
|
|
536
|
+
connectedCallback() {
|
|
537
|
+
this.unsubscribe = store.subscribe(this.handleStateChange);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
disconnectedCallback() {
|
|
541
|
+
this.unsubscribe(); // Important!
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
## Debugging
|
|
547
|
+
|
|
548
|
+
### Enable Debug Logging
|
|
549
|
+
|
|
550
|
+
```typescript
|
|
551
|
+
import { enableDebug } from '@miura/miura-render';
|
|
552
|
+
|
|
553
|
+
enableDebug({
|
|
554
|
+
element: true, // Shows data flow logs
|
|
555
|
+
});
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
### DevTools Integration
|
|
559
|
+
|
|
560
|
+
```typescript
|
|
561
|
+
// Install Redux DevTools browser extension
|
|
562
|
+
store.use(createDevToolsMiddleware('MyApp'));
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
### Debug Information
|
package/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './src/miura-data-flow';
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@miurajs/miura-data-flow",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"types": "index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./index.d.ts",
|
|
10
|
+
"import": "./index.ts",
|
|
11
|
+
"require": "./index.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"test": "vitest",
|
|
17
|
+
"dev": "vitest watch"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@miura/miura-debugger": "workspace:*"
|
|
21
|
+
},
|
|
22
|
+
"optionalDependencies": {
|
|
23
|
+
"@aws-sdk/client-s3": "^3.1005.0",
|
|
24
|
+
"graphql-request": "^6.1.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"typescript": "^5.0.0",
|
|
28
|
+
"vitest": "^1.0.0"
|
|
29
|
+
}
|
|
30
|
+
}
|