@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/src/store.ts
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { debugLog } from '@miura/miura-debugger';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Store state interface
|
|
5
|
+
*/
|
|
6
|
+
export interface StoreState {
|
|
7
|
+
[key: string]: unknown;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Store actions interface
|
|
12
|
+
*/
|
|
13
|
+
export type StoreAction<T extends StoreState> = (
|
|
14
|
+
state: T,
|
|
15
|
+
...args: any[]
|
|
16
|
+
) => Partial<T> | Promise<Partial<T>>;
|
|
17
|
+
|
|
18
|
+
export interface StoreActions<T extends StoreState> {
|
|
19
|
+
[key: string]: StoreAction<T>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Store middleware interface
|
|
24
|
+
*/
|
|
25
|
+
export interface StoreMiddleware {
|
|
26
|
+
name: string;
|
|
27
|
+
before?: (action: string, args: unknown[], state: StoreState) => void | Promise<void>;
|
|
28
|
+
after?: (action: string, args: unknown[], state: StoreState, result: unknown) => void | Promise<void>;
|
|
29
|
+
error?: (action: string, args: unknown[], error: Error) => void | Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Store subscriber
|
|
34
|
+
*/
|
|
35
|
+
export interface StoreSubscriber {
|
|
36
|
+
id: string;
|
|
37
|
+
selector?: (state: StoreState) => unknown;
|
|
38
|
+
callback: (state: StoreState, prevState: StoreState) => void;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Modern Store Implementation
|
|
43
|
+
* Combines the best of Redux, Zustand, and modern patterns
|
|
44
|
+
*/
|
|
45
|
+
export class Store<T extends StoreState = StoreState> {
|
|
46
|
+
private state: T;
|
|
47
|
+
private actions: StoreActions<T> = {};
|
|
48
|
+
private subscribers = new Map<string, StoreSubscriber>();
|
|
49
|
+
private middlewares: StoreMiddleware[] = [];
|
|
50
|
+
private isUpdating = false;
|
|
51
|
+
private updateQueue: Array<() => void> = [];
|
|
52
|
+
private subscriberIdCounter = 0;
|
|
53
|
+
|
|
54
|
+
constructor(initialState: T) {
|
|
55
|
+
this.state = { ...initialState };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get current state
|
|
60
|
+
*/
|
|
61
|
+
getState(): T {
|
|
62
|
+
return { ...this.state };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Get a specific property from state
|
|
67
|
+
*/
|
|
68
|
+
get<K extends keyof T>(key: K): T[K] {
|
|
69
|
+
return this.state[key];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Set state (immutable update)
|
|
74
|
+
*/
|
|
75
|
+
setState(updater: Partial<T> | ((state: T) => Partial<T>)): void {
|
|
76
|
+
const newState = typeof updater === 'function' ? updater(this.state) : updater;
|
|
77
|
+
|
|
78
|
+
this.updateState(newState as Partial<T>);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Define actions (like Redux actions but simpler)
|
|
83
|
+
*/
|
|
84
|
+
defineActions(actions: StoreActions<T>): void {
|
|
85
|
+
this.actions = { ...this.actions, ...actions };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Execute an action
|
|
90
|
+
*/
|
|
91
|
+
async dispatch(action: string, ...args: unknown[]): Promise<void> {
|
|
92
|
+
if (!this.actions[action]) {
|
|
93
|
+
throw new Error(`Action '${action}' not found`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
// Run before middlewares
|
|
98
|
+
for (const middleware of this.middlewares) {
|
|
99
|
+
if (middleware.before) {
|
|
100
|
+
await middleware.before(action, args, this.state);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Execute action and update state
|
|
105
|
+
const result = await this.actions[action](this.state, ...args);
|
|
106
|
+
this.updateState(result as Partial<T>);
|
|
107
|
+
|
|
108
|
+
// Run after middlewares
|
|
109
|
+
for (const middleware of this.middlewares) {
|
|
110
|
+
if (middleware.after) {
|
|
111
|
+
await middleware.after(action, args, this.state, result);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
} catch (error) {
|
|
115
|
+
// Run error middlewares
|
|
116
|
+
for (const middleware of this.middlewares) {
|
|
117
|
+
if (middleware.error) {
|
|
118
|
+
await middleware.error(action, args, error as Error);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Subscribe to state changes
|
|
127
|
+
*/
|
|
128
|
+
subscribe(
|
|
129
|
+
callback: (state: T, prevState: T) => void,
|
|
130
|
+
selector?: (state: T) => unknown
|
|
131
|
+
): () => void {
|
|
132
|
+
const id = `subscriber_${++this.subscriberIdCounter}`;
|
|
133
|
+
const subscriber: StoreSubscriber = {
|
|
134
|
+
id,
|
|
135
|
+
selector: selector as (state: StoreState) => unknown,
|
|
136
|
+
callback: callback as (state: StoreState, prevState: StoreState) => void
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
this.subscribers.set(id, subscriber);
|
|
140
|
+
debugLog('element', 'Subscribed to store', { id, hasSelector: !!selector });
|
|
141
|
+
|
|
142
|
+
// Return unsubscribe function
|
|
143
|
+
return () => {
|
|
144
|
+
this.subscribers.delete(id);
|
|
145
|
+
debugLog('element', 'Unsubscribed from store', { id });
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Subscribe to specific property changes
|
|
151
|
+
*/
|
|
152
|
+
subscribeTo<K extends keyof T>(
|
|
153
|
+
key: K,
|
|
154
|
+
callback: (value: T[K], prevValue: T[K]) => void
|
|
155
|
+
): () => void {
|
|
156
|
+
return this.subscribe(
|
|
157
|
+
(state, prevState) => {
|
|
158
|
+
if (state[key] !== prevState[key]) {
|
|
159
|
+
callback(state[key], prevState[key]);
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
(state) => state[key]
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Add middleware
|
|
168
|
+
*/
|
|
169
|
+
use(middleware: StoreMiddleware): void {
|
|
170
|
+
this.middlewares.push(middleware);
|
|
171
|
+
debugLog('element', 'Added middleware', { name: middleware.name });
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Update state and notify subscribers
|
|
176
|
+
*/
|
|
177
|
+
private updateState(updater: Partial<T>): void {
|
|
178
|
+
if (this.isUpdating) {
|
|
179
|
+
// Queue update if already updating
|
|
180
|
+
this.updateQueue.push(() => this.updateState(updater));
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
this.isUpdating = true;
|
|
185
|
+
const prevState = { ...this.state };
|
|
186
|
+
|
|
187
|
+
// Apply updates
|
|
188
|
+
this.state = { ...this.state, ...updater };
|
|
189
|
+
|
|
190
|
+
// Notify subscribers
|
|
191
|
+
this.notifySubscribers(prevState);
|
|
192
|
+
|
|
193
|
+
this.isUpdating = false;
|
|
194
|
+
|
|
195
|
+
// Process queued updates
|
|
196
|
+
while (this.updateQueue.length > 0) {
|
|
197
|
+
const queuedUpdate = this.updateQueue.shift();
|
|
198
|
+
if (queuedUpdate) {
|
|
199
|
+
queuedUpdate();
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Notify all subscribers
|
|
206
|
+
*/
|
|
207
|
+
private notifySubscribers(prevState: T): void {
|
|
208
|
+
for (const subscriber of this.subscribers.values()) {
|
|
209
|
+
try {
|
|
210
|
+
if (subscriber.selector) {
|
|
211
|
+
const currentValue = subscriber.selector(this.state);
|
|
212
|
+
const prevValue = subscriber.selector(prevState);
|
|
213
|
+
|
|
214
|
+
if (currentValue !== prevValue) {
|
|
215
|
+
subscriber.callback(this.state, prevState);
|
|
216
|
+
}
|
|
217
|
+
} else {
|
|
218
|
+
subscriber.callback(this.state, prevState);
|
|
219
|
+
}
|
|
220
|
+
} catch (error) {
|
|
221
|
+
console.error(`Error in store subscriber ${subscriber.id}:`, error);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Get debug information
|
|
228
|
+
*/
|
|
229
|
+
getDebugInfo() {
|
|
230
|
+
return {
|
|
231
|
+
state: this.getState(),
|
|
232
|
+
actions: Object.keys(this.actions),
|
|
233
|
+
subscribers: this.subscribers.size,
|
|
234
|
+
middlewares: this.middlewares.map(m => m.name)
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
}
|