kimu-core 0.4.1 → 0.5.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/.editorconfig +116 -30
- package/.gitattributes +81 -11
- package/.github/FUNDING.yml +8 -8
- package/.github/kimu-copilot-instructions.md +3779 -3779
- package/.github/workflows/deploy-demo.yml +39 -39
- package/.nvmrc +1 -0
- package/.prettierignore +44 -0
- package/.prettierrc +16 -0
- package/FUNDING.md +31 -31
- package/icon.svg +10 -10
- package/kimu-core-0.5.0.tgz +0 -0
- package/package.json +10 -3
- package/scripts/minify-css-assets.js +82 -82
- package/src/core/index.ts +47 -47
- package/src/core/kimu-global-styles.ts +136 -136
- package/src/core/kimu-reactive.ts +196 -196
- package/src/extensions/{kimu-home → app-root}/component.ts +5 -5
- package/src/extensions/extensions-manifest.json +4 -4
- package/src/main.ts +3 -3
- package/src/modules-repository/api-axios/CHANGELOG.md +48 -48
- package/src/modules-repository/api-axios/QUICK-REFERENCE.md +178 -178
- package/src/modules-repository/api-axios/README.md +304 -304
- package/src/modules-repository/api-axios/api-axios-service.ts +355 -355
- package/src/modules-repository/api-axios/examples.ts +293 -293
- package/src/modules-repository/api-axios/index.ts +19 -19
- package/src/modules-repository/api-axios/interfaces.ts +71 -71
- package/src/modules-repository/api-axios/module.ts +41 -41
- package/src/modules-repository/api-core/CHANGELOG.md +42 -42
- package/src/modules-repository/api-core/QUICK-REFERENCE.md +192 -192
- package/src/modules-repository/api-core/README.md +435 -435
- package/src/modules-repository/api-core/api-core-service.ts +289 -289
- package/src/modules-repository/api-core/examples.ts +432 -432
- package/src/modules-repository/api-core/index.ts +8 -8
- package/src/modules-repository/api-core/interfaces.ts +83 -83
- package/src/modules-repository/api-core/module.ts +30 -30
- package/src/modules-repository/event-bus/README.md +273 -273
- package/src/modules-repository/event-bus/event-bus-service.ts +176 -176
- package/src/modules-repository/event-bus/module.ts +30 -30
- package/src/modules-repository/notification/README.md +423 -423
- package/src/modules-repository/notification/module.ts +30 -30
- package/src/modules-repository/notification/notification-service.ts +436 -436
- package/src/modules-repository/router/README.it.md +61 -10
- package/src/modules-repository/router/README.md +61 -10
- package/src/modules-repository/router/router-config.ts.example +61 -0
- package/src/modules-repository/router/router.ts +18 -0
- package/src/modules-repository/state/README.md +409 -409
- package/src/modules-repository/state/module.ts +30 -30
- package/src/modules-repository/state/state-service.ts +296 -296
- package/src/modules-repository/theme/README.md +311 -267
- package/src/modules-repository/theme/module.ts +30 -30
- package/src/modules-repository/theme/pre-build.js +40 -40
- package/src/modules-repository/theme/theme-service.ts +411 -389
- package/src/modules-repository/theme/themes/theme-cherry-blossom.css +78 -78
- package/src/modules-repository/theme/themes/theme-cozy.css +111 -111
- package/src/modules-repository/theme/themes/theme-cyberpunk.css +150 -150
- package/src/modules-repository/theme/themes/theme-dark.css +79 -79
- package/src/modules-repository/theme/themes/theme-forest.css +171 -171
- package/src/modules-repository/theme/themes/theme-gold.css +100 -100
- package/src/modules-repository/theme/themes/theme-high-contrast.css +126 -126
- package/src/modules-repository/theme/themes/theme-lava.css +101 -101
- package/src/modules-repository/theme/themes/theme-lavender.css +90 -90
- package/src/modules-repository/theme/themes/theme-light.css +79 -79
- package/src/modules-repository/theme/themes/theme-matrix.css +103 -103
- package/src/modules-repository/theme/themes/theme-midnight.css +81 -81
- package/src/modules-repository/theme/themes/theme-nord.css +94 -94
- package/src/modules-repository/theme/themes/theme-ocean.css +84 -84
- package/src/modules-repository/theme/themes/theme-retro80s.css +343 -343
- package/src/modules-repository/theme/themes/theme-sunset.css +62 -62
- package/src/modules-repository/theme/themes-config-default.json +19 -0
- package/src/modules-repository/theme/themes-config.d.ts +27 -27
- package/src/modules-repository/theme/{themes-config.json → themes-config.json.example} +223 -213
- /package/src/extensions/{kimu-home → app-root}/lang/en.json +0 -0
- /package/src/extensions/{kimu-home → app-root}/lang/it.json +0 -0
- /package/src/extensions/{kimu-home → app-root}/style.css +0 -0
- /package/src/extensions/{kimu-home → app-root}/view.html +0 -0
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* State Management Module for KIMU-Core
|
|
3
|
-
*
|
|
4
|
-
* Provides reactive state management with observers.
|
|
5
|
-
*
|
|
6
|
-
* @module StateModule
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { KimuModule } from '../../core/kimu-module';
|
|
10
|
-
import { stateService } from './state-service';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* StateModule - Module class for state management integration
|
|
14
|
-
*/
|
|
15
|
-
export default class StateModule extends KimuModule {
|
|
16
|
-
constructor(name = 'state', version = '1.0.0', options?: any) {
|
|
17
|
-
super(name, version, options);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Get the state management service instance
|
|
22
|
-
*/
|
|
23
|
-
getService() {
|
|
24
|
-
return stateService;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Re-export for convenience
|
|
29
|
-
export { stateService } from './state-service';
|
|
30
|
-
export { StateService } from './state-service';
|
|
1
|
+
/**
|
|
2
|
+
* State Management Module for KIMU-Core
|
|
3
|
+
*
|
|
4
|
+
* Provides reactive state management with observers.
|
|
5
|
+
*
|
|
6
|
+
* @module StateModule
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { KimuModule } from '../../core/kimu-module';
|
|
10
|
+
import { stateService } from './state-service';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* StateModule - Module class for state management integration
|
|
14
|
+
*/
|
|
15
|
+
export default class StateModule extends KimuModule {
|
|
16
|
+
constructor(name = 'state', version = '1.0.0', options?: any) {
|
|
17
|
+
super(name, version, options);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Get the state management service instance
|
|
22
|
+
*/
|
|
23
|
+
getService() {
|
|
24
|
+
return stateService;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Re-export for convenience
|
|
29
|
+
export { stateService } from './state-service';
|
|
30
|
+
export { StateService } from './state-service';
|
|
@@ -1,296 +1,296 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* State Management Service for KIMU-Core
|
|
3
|
-
*
|
|
4
|
-
* Provides reactive state management with observers and computed properties.
|
|
5
|
-
* Allows components to share and react to state changes.
|
|
6
|
-
*
|
|
7
|
-
* @module StateService
|
|
8
|
-
* @version 1.0.0
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
export type StateListener<T = any> = (newValue: T, oldValue: T) => void;
|
|
12
|
-
export type ComputedGetter<T = any> = () => T;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* StateService - Reactive state management for KIMU applications
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* ```typescript
|
|
19
|
-
* import { stateService } from './modules/state/state-service';
|
|
20
|
-
*
|
|
21
|
-
* // Set initial state
|
|
22
|
-
* stateService.setState('user', { id: 1, name: 'John' });
|
|
23
|
-
*
|
|
24
|
-
* // Watch for changes
|
|
25
|
-
* stateService.watch('user', (newUser, oldUser) => {
|
|
26
|
-
* console.log('User changed:', newUser);
|
|
27
|
-
* });
|
|
28
|
-
*
|
|
29
|
-
* // Update state
|
|
30
|
-
* stateService.setState('user', { id: 1, name: 'Jane' });
|
|
31
|
-
* ```
|
|
32
|
-
*/
|
|
33
|
-
export class StateService {
|
|
34
|
-
private state: Map<string, any> = new Map();
|
|
35
|
-
private observers: Map<string, Set<StateListener>> = new Map();
|
|
36
|
-
private computed: Map<string, ComputedGetter> = new Map();
|
|
37
|
-
private computedCache: Map<string, any> = new Map();
|
|
38
|
-
private debugMode: boolean = false;
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Enable or disable debug mode for state logging
|
|
42
|
-
*/
|
|
43
|
-
setDebugMode(enabled: boolean): void {
|
|
44
|
-
this.debugMode = enabled;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Set a state value
|
|
49
|
-
*
|
|
50
|
-
* @param key - State key
|
|
51
|
-
* @param value - New value
|
|
52
|
-
*/
|
|
53
|
-
setState<T = any>(key: string, value: T): void {
|
|
54
|
-
const oldValue = this.state.get(key);
|
|
55
|
-
|
|
56
|
-
// Check if value actually changed
|
|
57
|
-
if (oldValue === value || this.deepEqual(oldValue, value)) {
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
this.state.set(key, value);
|
|
62
|
-
|
|
63
|
-
if (this.debugMode) {
|
|
64
|
-
console.log(`[State] Set: ${key}`, value);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Notify observers
|
|
68
|
-
this.notifyObservers(key, value, oldValue);
|
|
69
|
-
|
|
70
|
-
// Invalidate computed properties that may depend on this key
|
|
71
|
-
this.invalidateComputedCache();
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Get a state value
|
|
76
|
-
*
|
|
77
|
-
* @param key - State key
|
|
78
|
-
* @param defaultValue - Default value if key doesn't exist
|
|
79
|
-
* @returns Current state value
|
|
80
|
-
*/
|
|
81
|
-
getState<T = any>(key: string, defaultValue?: T): T | undefined {
|
|
82
|
-
return this.state.has(key) ? this.state.get(key) : defaultValue;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Update nested state properties
|
|
87
|
-
*
|
|
88
|
-
* @param key - State key
|
|
89
|
-
* @param updates - Partial updates to merge
|
|
90
|
-
*/
|
|
91
|
-
updateState<T = any>(key: string, updates: Partial<T>): void {
|
|
92
|
-
const current = this.getState<T>(key) || {} as T;
|
|
93
|
-
const updated = { ...current, ...updates };
|
|
94
|
-
this.setState(key, updated);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Delete a state key
|
|
99
|
-
*
|
|
100
|
-
* @param key - State key to delete
|
|
101
|
-
*/
|
|
102
|
-
deleteState(key: string): void {
|
|
103
|
-
const oldValue = this.state.get(key);
|
|
104
|
-
this.state.delete(key);
|
|
105
|
-
|
|
106
|
-
if (this.debugMode) {
|
|
107
|
-
console.log(`[State] Deleted: ${key}`);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Notify observers with undefined as new value
|
|
111
|
-
this.notifyObservers(key, undefined, oldValue);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Check if a state key exists
|
|
116
|
-
*
|
|
117
|
-
* @param key - State key
|
|
118
|
-
* @returns True if key exists
|
|
119
|
-
*/
|
|
120
|
-
hasState(key: string): boolean {
|
|
121
|
-
return this.state.has(key);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Watch for state changes
|
|
126
|
-
*
|
|
127
|
-
* @param key - State key to watch
|
|
128
|
-
* @param listener - Callback when state changes
|
|
129
|
-
* @returns Unwatch function
|
|
130
|
-
*/
|
|
131
|
-
watch<T = any>(key: string, listener: StateListener<T>): () => void {
|
|
132
|
-
if (!this.observers.has(key)) {
|
|
133
|
-
this.observers.set(key, new Set());
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
this.observers.get(key)!.add(listener);
|
|
137
|
-
|
|
138
|
-
if (this.debugMode) {
|
|
139
|
-
console.log(`[State] Watching: ${key}`);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Return unwatch function
|
|
143
|
-
return () => this.unwatch(key, listener);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Stop watching a state key
|
|
148
|
-
*
|
|
149
|
-
* @param key - State key
|
|
150
|
-
* @param listener - Listener to remove
|
|
151
|
-
*/
|
|
152
|
-
unwatch(key: string, listener: StateListener): void {
|
|
153
|
-
if (this.observers.has(key)) {
|
|
154
|
-
this.observers.get(key)!.delete(listener);
|
|
155
|
-
|
|
156
|
-
if (this.observers.get(key)!.size === 0) {
|
|
157
|
-
this.observers.delete(key);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
if (this.debugMode) {
|
|
161
|
-
console.log(`[State] Unwatched: ${key}`);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Define a computed property
|
|
168
|
-
*
|
|
169
|
-
* @param key - Computed property key
|
|
170
|
-
* @param getter - Function that computes the value
|
|
171
|
-
*/
|
|
172
|
-
defineComputed<T = any>(key: string, getter: ComputedGetter<T>): void {
|
|
173
|
-
this.computed.set(key, getter);
|
|
174
|
-
this.computedCache.delete(key); // Invalidate cache
|
|
175
|
-
|
|
176
|
-
if (this.debugMode) {
|
|
177
|
-
console.log(`[State] Computed defined: ${key}`);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Get a computed property value (cached)
|
|
183
|
-
*
|
|
184
|
-
* @param key - Computed property key
|
|
185
|
-
* @returns Computed value
|
|
186
|
-
*/
|
|
187
|
-
getComputed<T = any>(key: string): T | undefined {
|
|
188
|
-
if (!this.computed.has(key)) {
|
|
189
|
-
return undefined;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Return cached value if available
|
|
193
|
-
if (this.computedCache.has(key)) {
|
|
194
|
-
return this.computedCache.get(key);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Compute and cache
|
|
198
|
-
const getter = this.computed.get(key)!;
|
|
199
|
-
const value = getter();
|
|
200
|
-
this.computedCache.set(key, value);
|
|
201
|
-
|
|
202
|
-
if (this.debugMode) {
|
|
203
|
-
console.log(`[State] Computed: ${key}`, value);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
return value;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Get all state as a plain object
|
|
211
|
-
*/
|
|
212
|
-
getAll(): Record<string, any> {
|
|
213
|
-
return Object.fromEntries(this.state.entries());
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Clear all state
|
|
218
|
-
*/
|
|
219
|
-
clear(): void {
|
|
220
|
-
this.state.clear();
|
|
221
|
-
this.observers.clear();
|
|
222
|
-
this.computed.clear();
|
|
223
|
-
this.computedCache.clear();
|
|
224
|
-
|
|
225
|
-
if (this.debugMode) {
|
|
226
|
-
console.log('[State] Cleared all state');
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Reset state to initial values
|
|
232
|
-
*
|
|
233
|
-
* @param initialState - Initial state object
|
|
234
|
-
*/
|
|
235
|
-
reset(initialState: Record<string, any> = {}): void {
|
|
236
|
-
this.clear();
|
|
237
|
-
Object.entries(initialState).forEach(([key, value]) => {
|
|
238
|
-
this.setState(key, value);
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* Subscribe to multiple state keys
|
|
244
|
-
*
|
|
245
|
-
* @param keys - Array of state keys
|
|
246
|
-
* @param listener - Callback when any key changes
|
|
247
|
-
* @returns Unsubscribe function
|
|
248
|
-
*/
|
|
249
|
-
watchMultiple(keys: string[], listener: (key: string, newValue: any, oldValue: any) => void): () => void {
|
|
250
|
-
const unwatchers = keys.map(key =>
|
|
251
|
-
this.watch(key, (newValue, oldValue) => listener(key, newValue, oldValue))
|
|
252
|
-
);
|
|
253
|
-
|
|
254
|
-
return () => unwatchers.forEach(unwatch => unwatch());
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// Private helpers
|
|
258
|
-
|
|
259
|
-
private notifyObservers(key: string, newValue: any, oldValue: any): void {
|
|
260
|
-
if (this.observers.has(key)) {
|
|
261
|
-
const listeners = Array.from(this.observers.get(key)!);
|
|
262
|
-
listeners.forEach(listener => {
|
|
263
|
-
try {
|
|
264
|
-
listener(newValue, oldValue);
|
|
265
|
-
} catch (error) {
|
|
266
|
-
console.error(`[State] Error in observer for "${key}":`, error);
|
|
267
|
-
}
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
private invalidateComputedCache(): void {
|
|
273
|
-
this.computedCache.clear();
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
private deepEqual(a: any, b: any): boolean {
|
|
277
|
-
if (a === b) return true;
|
|
278
|
-
if (a == null || b == null) return false;
|
|
279
|
-
if (typeof a !== 'object' || typeof b !== 'object') return false;
|
|
280
|
-
|
|
281
|
-
const keysA = Object.keys(a);
|
|
282
|
-
const keysB = Object.keys(b);
|
|
283
|
-
|
|
284
|
-
if (keysA.length !== keysB.length) return false;
|
|
285
|
-
|
|
286
|
-
for (const key of keysA) {
|
|
287
|
-
if (!keysB.includes(key)) return false;
|
|
288
|
-
if (!this.deepEqual(a[key], b[key])) return false;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
return true;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// Export singleton instance
|
|
296
|
-
export const stateService = new StateService();
|
|
1
|
+
/**
|
|
2
|
+
* State Management Service for KIMU-Core
|
|
3
|
+
*
|
|
4
|
+
* Provides reactive state management with observers and computed properties.
|
|
5
|
+
* Allows components to share and react to state changes.
|
|
6
|
+
*
|
|
7
|
+
* @module StateService
|
|
8
|
+
* @version 1.0.0
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export type StateListener<T = any> = (newValue: T, oldValue: T) => void;
|
|
12
|
+
export type ComputedGetter<T = any> = () => T;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* StateService - Reactive state management for KIMU applications
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* import { stateService } from './modules/state/state-service';
|
|
20
|
+
*
|
|
21
|
+
* // Set initial state
|
|
22
|
+
* stateService.setState('user', { id: 1, name: 'John' });
|
|
23
|
+
*
|
|
24
|
+
* // Watch for changes
|
|
25
|
+
* stateService.watch('user', (newUser, oldUser) => {
|
|
26
|
+
* console.log('User changed:', newUser);
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* // Update state
|
|
30
|
+
* stateService.setState('user', { id: 1, name: 'Jane' });
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export class StateService {
|
|
34
|
+
private state: Map<string, any> = new Map();
|
|
35
|
+
private observers: Map<string, Set<StateListener>> = new Map();
|
|
36
|
+
private computed: Map<string, ComputedGetter> = new Map();
|
|
37
|
+
private computedCache: Map<string, any> = new Map();
|
|
38
|
+
private debugMode: boolean = false;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Enable or disable debug mode for state logging
|
|
42
|
+
*/
|
|
43
|
+
setDebugMode(enabled: boolean): void {
|
|
44
|
+
this.debugMode = enabled;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Set a state value
|
|
49
|
+
*
|
|
50
|
+
* @param key - State key
|
|
51
|
+
* @param value - New value
|
|
52
|
+
*/
|
|
53
|
+
setState<T = any>(key: string, value: T): void {
|
|
54
|
+
const oldValue = this.state.get(key);
|
|
55
|
+
|
|
56
|
+
// Check if value actually changed
|
|
57
|
+
if (oldValue === value || this.deepEqual(oldValue, value)) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
this.state.set(key, value);
|
|
62
|
+
|
|
63
|
+
if (this.debugMode) {
|
|
64
|
+
console.log(`[State] Set: ${key}`, value);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Notify observers
|
|
68
|
+
this.notifyObservers(key, value, oldValue);
|
|
69
|
+
|
|
70
|
+
// Invalidate computed properties that may depend on this key
|
|
71
|
+
this.invalidateComputedCache();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Get a state value
|
|
76
|
+
*
|
|
77
|
+
* @param key - State key
|
|
78
|
+
* @param defaultValue - Default value if key doesn't exist
|
|
79
|
+
* @returns Current state value
|
|
80
|
+
*/
|
|
81
|
+
getState<T = any>(key: string, defaultValue?: T): T | undefined {
|
|
82
|
+
return this.state.has(key) ? this.state.get(key) : defaultValue;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Update nested state properties
|
|
87
|
+
*
|
|
88
|
+
* @param key - State key
|
|
89
|
+
* @param updates - Partial updates to merge
|
|
90
|
+
*/
|
|
91
|
+
updateState<T = any>(key: string, updates: Partial<T>): void {
|
|
92
|
+
const current = this.getState<T>(key) || {} as T;
|
|
93
|
+
const updated = { ...current, ...updates };
|
|
94
|
+
this.setState(key, updated);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Delete a state key
|
|
99
|
+
*
|
|
100
|
+
* @param key - State key to delete
|
|
101
|
+
*/
|
|
102
|
+
deleteState(key: string): void {
|
|
103
|
+
const oldValue = this.state.get(key);
|
|
104
|
+
this.state.delete(key);
|
|
105
|
+
|
|
106
|
+
if (this.debugMode) {
|
|
107
|
+
console.log(`[State] Deleted: ${key}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Notify observers with undefined as new value
|
|
111
|
+
this.notifyObservers(key, undefined, oldValue);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Check if a state key exists
|
|
116
|
+
*
|
|
117
|
+
* @param key - State key
|
|
118
|
+
* @returns True if key exists
|
|
119
|
+
*/
|
|
120
|
+
hasState(key: string): boolean {
|
|
121
|
+
return this.state.has(key);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Watch for state changes
|
|
126
|
+
*
|
|
127
|
+
* @param key - State key to watch
|
|
128
|
+
* @param listener - Callback when state changes
|
|
129
|
+
* @returns Unwatch function
|
|
130
|
+
*/
|
|
131
|
+
watch<T = any>(key: string, listener: StateListener<T>): () => void {
|
|
132
|
+
if (!this.observers.has(key)) {
|
|
133
|
+
this.observers.set(key, new Set());
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
this.observers.get(key)!.add(listener);
|
|
137
|
+
|
|
138
|
+
if (this.debugMode) {
|
|
139
|
+
console.log(`[State] Watching: ${key}`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Return unwatch function
|
|
143
|
+
return () => this.unwatch(key, listener);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Stop watching a state key
|
|
148
|
+
*
|
|
149
|
+
* @param key - State key
|
|
150
|
+
* @param listener - Listener to remove
|
|
151
|
+
*/
|
|
152
|
+
unwatch(key: string, listener: StateListener): void {
|
|
153
|
+
if (this.observers.has(key)) {
|
|
154
|
+
this.observers.get(key)!.delete(listener);
|
|
155
|
+
|
|
156
|
+
if (this.observers.get(key)!.size === 0) {
|
|
157
|
+
this.observers.delete(key);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (this.debugMode) {
|
|
161
|
+
console.log(`[State] Unwatched: ${key}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Define a computed property
|
|
168
|
+
*
|
|
169
|
+
* @param key - Computed property key
|
|
170
|
+
* @param getter - Function that computes the value
|
|
171
|
+
*/
|
|
172
|
+
defineComputed<T = any>(key: string, getter: ComputedGetter<T>): void {
|
|
173
|
+
this.computed.set(key, getter);
|
|
174
|
+
this.computedCache.delete(key); // Invalidate cache
|
|
175
|
+
|
|
176
|
+
if (this.debugMode) {
|
|
177
|
+
console.log(`[State] Computed defined: ${key}`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Get a computed property value (cached)
|
|
183
|
+
*
|
|
184
|
+
* @param key - Computed property key
|
|
185
|
+
* @returns Computed value
|
|
186
|
+
*/
|
|
187
|
+
getComputed<T = any>(key: string): T | undefined {
|
|
188
|
+
if (!this.computed.has(key)) {
|
|
189
|
+
return undefined;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Return cached value if available
|
|
193
|
+
if (this.computedCache.has(key)) {
|
|
194
|
+
return this.computedCache.get(key);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Compute and cache
|
|
198
|
+
const getter = this.computed.get(key)!;
|
|
199
|
+
const value = getter();
|
|
200
|
+
this.computedCache.set(key, value);
|
|
201
|
+
|
|
202
|
+
if (this.debugMode) {
|
|
203
|
+
console.log(`[State] Computed: ${key}`, value);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return value;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Get all state as a plain object
|
|
211
|
+
*/
|
|
212
|
+
getAll(): Record<string, any> {
|
|
213
|
+
return Object.fromEntries(this.state.entries());
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Clear all state
|
|
218
|
+
*/
|
|
219
|
+
clear(): void {
|
|
220
|
+
this.state.clear();
|
|
221
|
+
this.observers.clear();
|
|
222
|
+
this.computed.clear();
|
|
223
|
+
this.computedCache.clear();
|
|
224
|
+
|
|
225
|
+
if (this.debugMode) {
|
|
226
|
+
console.log('[State] Cleared all state');
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Reset state to initial values
|
|
232
|
+
*
|
|
233
|
+
* @param initialState - Initial state object
|
|
234
|
+
*/
|
|
235
|
+
reset(initialState: Record<string, any> = {}): void {
|
|
236
|
+
this.clear();
|
|
237
|
+
Object.entries(initialState).forEach(([key, value]) => {
|
|
238
|
+
this.setState(key, value);
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Subscribe to multiple state keys
|
|
244
|
+
*
|
|
245
|
+
* @param keys - Array of state keys
|
|
246
|
+
* @param listener - Callback when any key changes
|
|
247
|
+
* @returns Unsubscribe function
|
|
248
|
+
*/
|
|
249
|
+
watchMultiple(keys: string[], listener: (key: string, newValue: any, oldValue: any) => void): () => void {
|
|
250
|
+
const unwatchers = keys.map(key =>
|
|
251
|
+
this.watch(key, (newValue, oldValue) => listener(key, newValue, oldValue))
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
return () => unwatchers.forEach(unwatch => unwatch());
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Private helpers
|
|
258
|
+
|
|
259
|
+
private notifyObservers(key: string, newValue: any, oldValue: any): void {
|
|
260
|
+
if (this.observers.has(key)) {
|
|
261
|
+
const listeners = Array.from(this.observers.get(key)!);
|
|
262
|
+
listeners.forEach(listener => {
|
|
263
|
+
try {
|
|
264
|
+
listener(newValue, oldValue);
|
|
265
|
+
} catch (error) {
|
|
266
|
+
console.error(`[State] Error in observer for "${key}":`, error);
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
private invalidateComputedCache(): void {
|
|
273
|
+
this.computedCache.clear();
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
private deepEqual(a: any, b: any): boolean {
|
|
277
|
+
if (a === b) return true;
|
|
278
|
+
if (a == null || b == null) return false;
|
|
279
|
+
if (typeof a !== 'object' || typeof b !== 'object') return false;
|
|
280
|
+
|
|
281
|
+
const keysA = Object.keys(a);
|
|
282
|
+
const keysB = Object.keys(b);
|
|
283
|
+
|
|
284
|
+
if (keysA.length !== keysB.length) return false;
|
|
285
|
+
|
|
286
|
+
for (const key of keysA) {
|
|
287
|
+
if (!keysB.includes(key)) return false;
|
|
288
|
+
if (!this.deepEqual(a[key], b[key])) return false;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return true;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Export singleton instance
|
|
296
|
+
export const stateService = new StateService();
|