@coherent.js/core 1.0.0-beta.2
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/LICENSE +21 -0
- package/README.md +130 -0
- package/dist/coherent.d.ts +472 -0
- package/dist/coherent.d.ts.map +1 -0
- package/dist/coherent.js +590 -0
- package/dist/coherent.js.map +1 -0
- package/dist/components/component-system.d.ts +1138 -0
- package/dist/components/component-system.d.ts.map +1 -0
- package/dist/components/component-system.js +2220 -0
- package/dist/components/component-system.js.map +1 -0
- package/dist/components/lifecycle.d.ts +212 -0
- package/dist/components/lifecycle.d.ts.map +1 -0
- package/dist/components/lifecycle.js +525 -0
- package/dist/components/lifecycle.js.map +1 -0
- package/dist/core/html-utils.d.ts +14 -0
- package/dist/core/html-utils.d.ts.map +1 -0
- package/dist/core/html-utils.js +166 -0
- package/dist/core/html-utils.js.map +1 -0
- package/dist/core/object-factory.d.ts +38 -0
- package/dist/core/object-factory.d.ts.map +1 -0
- package/dist/core/object-factory.js +63 -0
- package/dist/core/object-factory.js.map +1 -0
- package/dist/core/object-utils.d.ts +77 -0
- package/dist/core/object-utils.d.ts.map +1 -0
- package/dist/core/object-utils.js +502 -0
- package/dist/core/object-utils.js.map +1 -0
- package/dist/dev/dev-tools.d.ts +194 -0
- package/dist/dev/dev-tools.d.ts.map +1 -0
- package/dist/dev/dev-tools.js +846 -0
- package/dist/dev/dev-tools.js.map +1 -0
- package/dist/forms/validation.d.ts +271 -0
- package/dist/forms/validation.d.ts.map +1 -0
- package/dist/forms/validation.js +573 -0
- package/dist/forms/validation.js.map +1 -0
- package/dist/index.cjs +5281 -0
- package/dist/index.cjs.map +7 -0
- package/dist/index.js +5204 -0
- package/dist/index.js.map +7 -0
- package/dist/performance/bundle-optimizer.d.ts +95 -0
- package/dist/performance/bundle-optimizer.d.ts.map +1 -0
- package/dist/performance/bundle-optimizer.js +192 -0
- package/dist/performance/bundle-optimizer.js.map +1 -0
- package/dist/performance/cache-manager.d.ts +107 -0
- package/dist/performance/cache-manager.d.ts.map +1 -0
- package/dist/performance/cache-manager.js +314 -0
- package/dist/performance/cache-manager.js.map +1 -0
- package/dist/performance/component-cache.d.ts +120 -0
- package/dist/performance/component-cache.d.ts.map +1 -0
- package/dist/performance/component-cache.js +364 -0
- package/dist/performance/component-cache.js.map +1 -0
- package/dist/performance/monitor.d.ts +189 -0
- package/dist/performance/monitor.d.ts.map +1 -0
- package/dist/performance/monitor.js +347 -0
- package/dist/performance/monitor.js.map +1 -0
- package/dist/rendering/base-renderer.d.ts +140 -0
- package/dist/rendering/base-renderer.d.ts.map +1 -0
- package/dist/rendering/base-renderer.js +409 -0
- package/dist/rendering/base-renderer.js.map +1 -0
- package/dist/rendering/css-manager.d.ts +73 -0
- package/dist/rendering/css-manager.d.ts.map +1 -0
- package/dist/rendering/css-manager.js +176 -0
- package/dist/rendering/css-manager.js.map +1 -0
- package/dist/rendering/dom-renderer.d.ts +62 -0
- package/dist/rendering/dom-renderer.d.ts.map +1 -0
- package/dist/rendering/dom-renderer.js +252 -0
- package/dist/rendering/dom-renderer.js.map +1 -0
- package/dist/rendering/html-renderer.d.ts +67 -0
- package/dist/rendering/html-renderer.d.ts.map +1 -0
- package/dist/rendering/html-renderer.js +444 -0
- package/dist/rendering/html-renderer.js.map +1 -0
- package/dist/rendering/renderer-config.d.ts +282 -0
- package/dist/rendering/renderer-config.d.ts.map +1 -0
- package/dist/rendering/renderer-config.js +144 -0
- package/dist/rendering/renderer-config.js.map +1 -0
- package/dist/rendering/streaming-renderer.d.ts +117 -0
- package/dist/rendering/streaming-renderer.d.ts.map +1 -0
- package/dist/rendering/streaming-renderer.js +326 -0
- package/dist/rendering/streaming-renderer.js.map +1 -0
- package/dist/rendering/vdom-diff.d.ts +47 -0
- package/dist/rendering/vdom-diff.d.ts.map +1 -0
- package/dist/rendering/vdom-diff.js +416 -0
- package/dist/rendering/vdom-diff.js.map +1 -0
- package/dist/routing/router.d.ts +241 -0
- package/dist/routing/router.d.ts.map +1 -0
- package/dist/routing/router.js +648 -0
- package/dist/routing/router.js.map +1 -0
- package/dist/state/reactive-state.d.ts +166 -0
- package/dist/state/reactive-state.d.ts.map +1 -0
- package/dist/state/reactive-state.js +546 -0
- package/dist/state/reactive-state.js.map +1 -0
- package/dist/state/state-manager.d.ts +45 -0
- package/dist/state/state-manager.d.ts.map +1 -0
- package/dist/state/state-manager.js +151 -0
- package/dist/state/state-manager.js.map +1 -0
- package/dist/types/constants.d.ts +8 -0
- package/dist/types/constants.d.ts.map +1 -0
- package/dist/types/constants.js +36 -0
- package/dist/types/constants.js.map +1 -0
- package/dist/utils/dependency-utils.d.ts +43 -0
- package/dist/utils/dependency-utils.d.ts.map +1 -0
- package/dist/utils/dependency-utils.js +105 -0
- package/dist/utils/dependency-utils.js.map +1 -0
- package/dist/utils/error-handler.d.ts +148 -0
- package/dist/utils/error-handler.d.ts.map +1 -0
- package/dist/utils/error-handler.js +468 -0
- package/dist/utils/error-handler.js.map +1 -0
- package/dist/utils/normalization.d.ts +3 -0
- package/dist/utils/normalization.d.ts.map +1 -0
- package/dist/utils/normalization.js +24 -0
- package/dist/utils/normalization.js.map +1 -0
- package/dist/utils/validation.d.ts +10 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +32 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +44 -0
- package/types/index.d.ts +734 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create reactive state store
|
|
3
|
+
*/
|
|
4
|
+
export function createReactiveState(initialState: any, options?: {}): ReactiveState;
|
|
5
|
+
/**
|
|
6
|
+
* Create observable value
|
|
7
|
+
*/
|
|
8
|
+
export function observable(value: any, options?: {}): Observable;
|
|
9
|
+
/**
|
|
10
|
+
* Create computed property
|
|
11
|
+
*/
|
|
12
|
+
export function computed(getter: any, options?: {}): Computed;
|
|
13
|
+
/**
|
|
14
|
+
* Observable wrapper for tracking state changes
|
|
15
|
+
*/
|
|
16
|
+
export class Observable {
|
|
17
|
+
constructor(value: any, options?: {});
|
|
18
|
+
_value: any;
|
|
19
|
+
_observers: Set<any>;
|
|
20
|
+
_computedDependents: Set<any>;
|
|
21
|
+
_options: {
|
|
22
|
+
deep: boolean;
|
|
23
|
+
immediate: boolean;
|
|
24
|
+
};
|
|
25
|
+
set value(newValue: any);
|
|
26
|
+
get value(): any;
|
|
27
|
+
watch(callback: any, options?: {}): () => void;
|
|
28
|
+
unwatch(observer: any): void;
|
|
29
|
+
unwatchAll(): void;
|
|
30
|
+
}
|
|
31
|
+
export namespace Observable {
|
|
32
|
+
let _currentComputed: any;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Reactive state container with advanced features
|
|
36
|
+
*/
|
|
37
|
+
export class ReactiveState {
|
|
38
|
+
constructor(initialState?: {}, options?: {});
|
|
39
|
+
_state: Map<any, any>;
|
|
40
|
+
_computed: Map<any, any>;
|
|
41
|
+
_watchers: Map<any, any>;
|
|
42
|
+
_middleware: any[];
|
|
43
|
+
_history: any[];
|
|
44
|
+
_options: {
|
|
45
|
+
enableHistory: boolean;
|
|
46
|
+
maxHistorySize: any;
|
|
47
|
+
enableMiddleware: boolean;
|
|
48
|
+
deep: boolean;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Get reactive state value
|
|
52
|
+
*/
|
|
53
|
+
get(key: any): any;
|
|
54
|
+
/**
|
|
55
|
+
* Set reactive state value
|
|
56
|
+
*/
|
|
57
|
+
set(key: any, value: any, options?: {}): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Check if state has a key
|
|
60
|
+
*/
|
|
61
|
+
has(key: any): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Delete state key
|
|
64
|
+
*/
|
|
65
|
+
delete(key: any): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Clear all state
|
|
68
|
+
*/
|
|
69
|
+
clear(): void;
|
|
70
|
+
/**
|
|
71
|
+
* Create computed property
|
|
72
|
+
*/
|
|
73
|
+
computed(key: any, getter: any, options?: {}): Computed;
|
|
74
|
+
/**
|
|
75
|
+
* Get computed property value
|
|
76
|
+
*/
|
|
77
|
+
getComputed(key: any): any;
|
|
78
|
+
/**
|
|
79
|
+
* Watch state changes
|
|
80
|
+
*/
|
|
81
|
+
watch(key: any, callback: any, options?: {}): any;
|
|
82
|
+
/**
|
|
83
|
+
* Watch computed expression
|
|
84
|
+
*/
|
|
85
|
+
_watchComputed(expression: any, callback: any, options?: {}): () => void;
|
|
86
|
+
/**
|
|
87
|
+
* Batch state updates
|
|
88
|
+
*/
|
|
89
|
+
batch(updates: any): any;
|
|
90
|
+
/**
|
|
91
|
+
* Subscribe to multiple state changes
|
|
92
|
+
*/
|
|
93
|
+
subscribe(keys: any, callback: any, options?: {}): () => void;
|
|
94
|
+
/**
|
|
95
|
+
* Add middleware for state changes
|
|
96
|
+
*/
|
|
97
|
+
use(middleware: any): void;
|
|
98
|
+
/**
|
|
99
|
+
* Run middleware chain
|
|
100
|
+
*/
|
|
101
|
+
_runMiddleware(action: any, context: any): any;
|
|
102
|
+
/**
|
|
103
|
+
* Add action to history
|
|
104
|
+
*/
|
|
105
|
+
_addToHistory(action: any, key: any, oldValue: any, newValue: any): void;
|
|
106
|
+
/**
|
|
107
|
+
* Get state history
|
|
108
|
+
*/
|
|
109
|
+
getHistory(limit?: number): any[];
|
|
110
|
+
/**
|
|
111
|
+
* Undo last action
|
|
112
|
+
*/
|
|
113
|
+
undo(): boolean;
|
|
114
|
+
/**
|
|
115
|
+
* Convert state to plain object
|
|
116
|
+
*/
|
|
117
|
+
toObject(): {};
|
|
118
|
+
/**
|
|
119
|
+
* Convert computed properties to object
|
|
120
|
+
*/
|
|
121
|
+
getComputedValues(): {};
|
|
122
|
+
/**
|
|
123
|
+
* Get state statistics
|
|
124
|
+
*/
|
|
125
|
+
getStats(): {
|
|
126
|
+
stateKeys: number;
|
|
127
|
+
computedKeys: number;
|
|
128
|
+
watcherKeys: number;
|
|
129
|
+
historyLength: number;
|
|
130
|
+
middlewareCount: number;
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Cleanup and destroy
|
|
134
|
+
*/
|
|
135
|
+
destroy(): void;
|
|
136
|
+
}
|
|
137
|
+
export namespace stateUtils {
|
|
138
|
+
/**
|
|
139
|
+
* Create a toggle state
|
|
140
|
+
*/
|
|
141
|
+
function toggle(initialValue?: boolean): Observable;
|
|
142
|
+
/**
|
|
143
|
+
* Create a counter state
|
|
144
|
+
*/
|
|
145
|
+
function counter(initialValue?: number): Observable;
|
|
146
|
+
/**
|
|
147
|
+
* Create an array state with utilities
|
|
148
|
+
*/
|
|
149
|
+
function array(initialArray?: any[]): Observable;
|
|
150
|
+
/**
|
|
151
|
+
* Create object state with deep reactivity
|
|
152
|
+
*/
|
|
153
|
+
function object(initialObject?: {}): ReactiveState;
|
|
154
|
+
}
|
|
155
|
+
export default ReactiveState;
|
|
156
|
+
/**
|
|
157
|
+
* Computed property implementation
|
|
158
|
+
*/
|
|
159
|
+
declare class Computed extends Observable {
|
|
160
|
+
_getter: any;
|
|
161
|
+
_cached: boolean;
|
|
162
|
+
_dirty: boolean;
|
|
163
|
+
_compute(): void;
|
|
164
|
+
_invalidate(): void;
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=reactive-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reactive-state.d.ts","sourceRoot":"","sources":["../../../../src/state/reactive-state.js"],"names":[],"mappings":"AAkhBA;;GAEG;AACH,oFAEC;AAED;;GAEG;AACH,iEAEC;AAED;;GAEG;AACH,8DAEC;AA9hBD;;GAEG;AACH;IACI,sCASC;IARG,YAAmB;IACnB,qBAA2B;IAC3B,8BAAoC;IACpC;;;MAIC;IAWL,yBAwBC;IAhCD,iBAMC;IA4BD,+CAkBC;IAED,6BAEC;IAED,mBAGC;CACJ;;;;AAoED;;GAEG;AACH;IACI,6CAkBC;IAjBG,sBAAuB;IACvB,yBAA0B;IAC1B,yBAA0B;IAC1B,mBAAqB;IACrB,gBAAkB;IAClB;;;;;MAMC;IAQL;;OAEG;IACH,mBAGC;IAED;;OAEG;IACH,iDA2BC;IAED;;OAEG;IACH,uBAEC;IAED;;OAEG;IACH,0BAaC;IAED;;OAEG;IACH,cAcC;IAED;;OAEG;IACH,wDASC;IAED;;OAEG;IACH,2BAGC;IAED;;OAEG;IACH,kDAoBC;IAED;;OAEG;IACH,yEAKC;IAED;;OAEG;IACH,yBA0BC;IAED;;OAEG;IACH,8DAoBC;IAED;;OAEG;IACH,2BAKC;IAED;;OAEG;IACH,+CAqBC;IAED;;OAEG;IACH,yEAeC;IAED;;OAEG;IACH,kCAEC;IAED;;OAEG;IACH,gBAiCC;IAED;;OAEG;IACH,eAMC;IAED;;OAEG;IACH,wBAMC;IAED;;OAEG;IACH;;;;;;MAQC;IAED;;OAEG;IACH,gBAeC;CACJ;;IA2BG;;OAEG;IACH,oDAMC;IAED;;OAEG;IACH,oDAYC;IAED;;OAEG;IACH,iDAkBC;IAED;;OAEG;IACH,mDAGC;;;AA9gBL;;GAEG;AACH;IAGQ,aAAqB;IACrB,iBAAoB;IACpB,gBAAkB;IAkBtB,iBA2BC;IAED,oBAKC;CACJ"}
|
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reactive State Management System for Coherent.js
|
|
3
|
+
* Provides computed properties, watchers, and reactive updates
|
|
4
|
+
*/
|
|
5
|
+
import { globalErrorHandler, StateError } from '../utils/error-handler.js';
|
|
6
|
+
/**
|
|
7
|
+
* Observable wrapper for tracking state changes
|
|
8
|
+
*/
|
|
9
|
+
export class Observable {
|
|
10
|
+
constructor(value, options = {}) {
|
|
11
|
+
this._value = value;
|
|
12
|
+
this._observers = new Set();
|
|
13
|
+
this._computedDependents = new Set();
|
|
14
|
+
this._options = {
|
|
15
|
+
deep: options.deep !== false,
|
|
16
|
+
immediate: options.immediate !== false,
|
|
17
|
+
...options
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
get value() {
|
|
21
|
+
// Track dependency for computed properties
|
|
22
|
+
if (Observable._currentComputed) {
|
|
23
|
+
this._computedDependents.add(Observable._currentComputed);
|
|
24
|
+
}
|
|
25
|
+
return this._value;
|
|
26
|
+
}
|
|
27
|
+
set value(newValue) {
|
|
28
|
+
if (this._value === newValue && !this._options.deep) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const oldValue = this._value;
|
|
32
|
+
this._value = newValue;
|
|
33
|
+
// Notify observers
|
|
34
|
+
this._observers.forEach(observer => {
|
|
35
|
+
try {
|
|
36
|
+
observer(newValue, oldValue);
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
globalErrorHandler.handle(error, {
|
|
40
|
+
type: 'watcher-error',
|
|
41
|
+
context: { newValue, oldValue }
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
// Update computed dependents
|
|
46
|
+
this._computedDependents.forEach(computed => {
|
|
47
|
+
computed._invalidate();
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
watch(callback, options = {}) {
|
|
51
|
+
if (typeof callback !== 'function') {
|
|
52
|
+
throw new StateError('Watch callback must be a function');
|
|
53
|
+
}
|
|
54
|
+
const observer = (newValue, oldValue) => {
|
|
55
|
+
callback(newValue, oldValue, () => this.unwatch(observer));
|
|
56
|
+
};
|
|
57
|
+
this._observers.add(observer);
|
|
58
|
+
// Call immediately if requested
|
|
59
|
+
if (options.immediate !== false) {
|
|
60
|
+
observer(this._value, undefined);
|
|
61
|
+
}
|
|
62
|
+
// Return unwatch function
|
|
63
|
+
return () => this.unwatch(observer);
|
|
64
|
+
}
|
|
65
|
+
unwatch(observer) {
|
|
66
|
+
this._observers.delete(observer);
|
|
67
|
+
}
|
|
68
|
+
unwatchAll() {
|
|
69
|
+
this._observers.clear();
|
|
70
|
+
this._computedDependents.clear();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Computed property implementation
|
|
75
|
+
*/
|
|
76
|
+
class Computed extends Observable {
|
|
77
|
+
constructor(getter, options = {}) {
|
|
78
|
+
super(undefined, options);
|
|
79
|
+
this._getter = getter;
|
|
80
|
+
this._cached = false;
|
|
81
|
+
this._dirty = true;
|
|
82
|
+
if (typeof getter !== 'function') {
|
|
83
|
+
throw new StateError('Computed getter must be a function');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
get value() {
|
|
87
|
+
if (this._dirty || !this._cached) {
|
|
88
|
+
this._compute();
|
|
89
|
+
}
|
|
90
|
+
return this._value;
|
|
91
|
+
}
|
|
92
|
+
set value(newValue) {
|
|
93
|
+
throw new StateError('Cannot set value on computed property');
|
|
94
|
+
}
|
|
95
|
+
_compute() {
|
|
96
|
+
const prevComputed = Observable._currentComputed;
|
|
97
|
+
Observable._currentComputed = this;
|
|
98
|
+
try {
|
|
99
|
+
const newValue = this._getter();
|
|
100
|
+
if (newValue !== this._value) {
|
|
101
|
+
const oldValue = this._value;
|
|
102
|
+
this._value = newValue;
|
|
103
|
+
// Notify observers
|
|
104
|
+
this._observers.forEach(observer => {
|
|
105
|
+
observer(newValue, oldValue);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
this._cached = true;
|
|
109
|
+
this._dirty = false;
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
globalErrorHandler.handle(error, {
|
|
113
|
+
type: 'computed-error',
|
|
114
|
+
context: { getter: this._getter.toString() }
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
Observable._currentComputed = prevComputed;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
_invalidate() {
|
|
122
|
+
this._dirty = true;
|
|
123
|
+
this._computedDependents.forEach(computed => {
|
|
124
|
+
computed._invalidate();
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// Static property for tracking current computed
|
|
129
|
+
Observable._currentComputed = null;
|
|
130
|
+
/**
|
|
131
|
+
* Reactive state container with advanced features
|
|
132
|
+
*/
|
|
133
|
+
export class ReactiveState {
|
|
134
|
+
constructor(initialState = {}, options = {}) {
|
|
135
|
+
this._state = new Map();
|
|
136
|
+
this._computed = new Map();
|
|
137
|
+
this._watchers = new Map();
|
|
138
|
+
this._middleware = [];
|
|
139
|
+
this._history = [];
|
|
140
|
+
this._options = {
|
|
141
|
+
enableHistory: options.enableHistory !== false,
|
|
142
|
+
maxHistorySize: options.maxHistorySize || 50,
|
|
143
|
+
enableMiddleware: options.enableMiddleware !== false,
|
|
144
|
+
deep: options.deep !== false,
|
|
145
|
+
...options
|
|
146
|
+
};
|
|
147
|
+
// Initialize state
|
|
148
|
+
Object.entries(initialState).forEach(([key, value]) => {
|
|
149
|
+
this.set(key, value);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Get reactive state value
|
|
154
|
+
*/
|
|
155
|
+
get(key) {
|
|
156
|
+
const observable = this._state.get(key);
|
|
157
|
+
return observable ? observable.value : undefined;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Set reactive state value
|
|
161
|
+
*/
|
|
162
|
+
set(key, value, options = {}) {
|
|
163
|
+
const _config = { ...this._options, ...options };
|
|
164
|
+
// Run middleware
|
|
165
|
+
if (config.enableMiddleware) {
|
|
166
|
+
const middlewareResult = this._runMiddleware('set', { key, value, oldValue: this.get(key) });
|
|
167
|
+
if (middlewareResult.cancelled) {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
value = middlewareResult.value !== undefined ? middlewareResult.value : value;
|
|
171
|
+
}
|
|
172
|
+
// Get or create observable
|
|
173
|
+
let observable = this._state.get(key);
|
|
174
|
+
if (!observable) {
|
|
175
|
+
observable = new Observable(value, config);
|
|
176
|
+
this._state.set(key, observable);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
// Record history
|
|
180
|
+
if (config.enableHistory) {
|
|
181
|
+
this._addToHistory('set', key, observable.value, value);
|
|
182
|
+
}
|
|
183
|
+
observable.value = value;
|
|
184
|
+
}
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Check if state has a key
|
|
189
|
+
*/
|
|
190
|
+
has(key) {
|
|
191
|
+
return this._state.has(key);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Delete state key
|
|
195
|
+
*/
|
|
196
|
+
delete(key) {
|
|
197
|
+
const observable = this._state.get(key);
|
|
198
|
+
if (observable) {
|
|
199
|
+
// Record history
|
|
200
|
+
if (this._options.enableHistory) {
|
|
201
|
+
this._addToHistory('delete', key, observable.value, undefined);
|
|
202
|
+
}
|
|
203
|
+
observable.unwatchAll();
|
|
204
|
+
this._state.delete(key);
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Clear all state
|
|
211
|
+
*/
|
|
212
|
+
clear() {
|
|
213
|
+
// Record history
|
|
214
|
+
if (this._options.enableHistory) {
|
|
215
|
+
this._addToHistory('clear', null, this.toObject(), {});
|
|
216
|
+
}
|
|
217
|
+
// Cleanup observables
|
|
218
|
+
for (const observable of this._state.values()) {
|
|
219
|
+
observable.unwatchAll();
|
|
220
|
+
}
|
|
221
|
+
this._state.clear();
|
|
222
|
+
this._computed.clear();
|
|
223
|
+
this._watchers.clear();
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Create computed property
|
|
227
|
+
*/
|
|
228
|
+
computed(key, getter, options = {}) {
|
|
229
|
+
if (typeof getter !== 'function') {
|
|
230
|
+
throw new StateError(`Computed property '${key}' getter must be a function`);
|
|
231
|
+
}
|
|
232
|
+
const computed = new Computed(getter, { ...this._options, ...options });
|
|
233
|
+
this._computed.set(key, computed);
|
|
234
|
+
return computed;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Get computed property value
|
|
238
|
+
*/
|
|
239
|
+
getComputed(key) {
|
|
240
|
+
const computed = this._computed.get(key);
|
|
241
|
+
return computed ? computed.value : undefined;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Watch state changes
|
|
245
|
+
*/
|
|
246
|
+
watch(key, callback, options = {}) {
|
|
247
|
+
if (typeof key === 'function') {
|
|
248
|
+
// Watch computed expression
|
|
249
|
+
return this._watchComputed(key, callback, options);
|
|
250
|
+
}
|
|
251
|
+
const observable = this._state.get(key);
|
|
252
|
+
if (!observable) {
|
|
253
|
+
throw new StateError(`Cannot watch undefined state key: ${key}`);
|
|
254
|
+
}
|
|
255
|
+
const unwatch = observable.watch(callback, options);
|
|
256
|
+
// Store watcher for cleanup
|
|
257
|
+
if (!this._watchers.has(key)) {
|
|
258
|
+
this._watchers.set(key, new Set());
|
|
259
|
+
}
|
|
260
|
+
this._watchers.get(key).add(unwatch);
|
|
261
|
+
return unwatch;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Watch computed expression
|
|
265
|
+
*/
|
|
266
|
+
_watchComputed(expression, callback, options = {}) {
|
|
267
|
+
const computed = new Computed(expression, options);
|
|
268
|
+
const unwatch = computed.watch(callback, options);
|
|
269
|
+
return unwatch;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Batch state updates
|
|
273
|
+
*/
|
|
274
|
+
batch(updates) {
|
|
275
|
+
if (typeof updates === 'function') {
|
|
276
|
+
// Batch function updates
|
|
277
|
+
const oldEnableHistory = this._options.enableHistory;
|
|
278
|
+
this._options.enableHistory = false;
|
|
279
|
+
try {
|
|
280
|
+
const result = updates(this);
|
|
281
|
+
// Record batch in history
|
|
282
|
+
if (oldEnableHistory) {
|
|
283
|
+
this._addToHistory('batch', null, null, this.toObject());
|
|
284
|
+
}
|
|
285
|
+
return result;
|
|
286
|
+
}
|
|
287
|
+
finally {
|
|
288
|
+
this._options.enableHistory = oldEnableHistory;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
else if (typeof updates === 'object') {
|
|
292
|
+
// Batch object updates
|
|
293
|
+
return this.batch(() => {
|
|
294
|
+
Object.entries(updates).forEach(([key, value]) => {
|
|
295
|
+
this.set(key, value);
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Subscribe to multiple state changes
|
|
302
|
+
*/
|
|
303
|
+
subscribe(keys, callback, options = {}) {
|
|
304
|
+
if (!Array.isArray(keys)) {
|
|
305
|
+
keys = [keys];
|
|
306
|
+
}
|
|
307
|
+
const unwatchers = keys.map(key => {
|
|
308
|
+
return this.watch(key, (newValue, oldValue) => {
|
|
309
|
+
callback({
|
|
310
|
+
key,
|
|
311
|
+
newValue,
|
|
312
|
+
oldValue,
|
|
313
|
+
state: this.toObject()
|
|
314
|
+
});
|
|
315
|
+
}, options);
|
|
316
|
+
});
|
|
317
|
+
// Return unsubscribe function
|
|
318
|
+
return () => {
|
|
319
|
+
unwatchers.forEach(unwatch => unwatch());
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Add middleware for state changes
|
|
324
|
+
*/
|
|
325
|
+
use(middleware) {
|
|
326
|
+
if (typeof middleware !== 'function') {
|
|
327
|
+
throw new StateError('Middleware must be a function');
|
|
328
|
+
}
|
|
329
|
+
this._middleware.push(middleware);
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Run middleware chain
|
|
333
|
+
*/
|
|
334
|
+
_runMiddleware(action, _context) {
|
|
335
|
+
let result = { ...context, cancelled: false };
|
|
336
|
+
for (const middleware of this._middleware) {
|
|
337
|
+
try {
|
|
338
|
+
const middlewareResult = middleware(action, result);
|
|
339
|
+
if (middlewareResult) {
|
|
340
|
+
result = { ...result, ...middlewareResult };
|
|
341
|
+
if (result.cancelled) {
|
|
342
|
+
break;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
catch (error) {
|
|
347
|
+
globalErrorHandler.handle(error, {
|
|
348
|
+
type: 'middleware-error',
|
|
349
|
+
context: { action, middleware: middleware.toString() }
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return result;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Add action to history
|
|
357
|
+
*/
|
|
358
|
+
_addToHistory(action, key, oldValue, newValue) {
|
|
359
|
+
if (!this._options.enableHistory)
|
|
360
|
+
return;
|
|
361
|
+
this._history.unshift({
|
|
362
|
+
action,
|
|
363
|
+
key,
|
|
364
|
+
oldValue,
|
|
365
|
+
newValue,
|
|
366
|
+
timestamp: Date.now()
|
|
367
|
+
});
|
|
368
|
+
// Limit history size
|
|
369
|
+
if (this._history.length > this._options.maxHistorySize) {
|
|
370
|
+
this._history = this._history.slice(0, this._options.maxHistorySize);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Get state history
|
|
375
|
+
*/
|
|
376
|
+
getHistory(limit = 10) {
|
|
377
|
+
return this._history.slice(0, limit);
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Undo last action
|
|
381
|
+
*/
|
|
382
|
+
undo() {
|
|
383
|
+
const lastAction = this._history.shift();
|
|
384
|
+
if (!lastAction)
|
|
385
|
+
return false;
|
|
386
|
+
const { action, key, oldValue } = lastAction;
|
|
387
|
+
// Temporarily disable history
|
|
388
|
+
const oldEnableHistory = this._options.enableHistory;
|
|
389
|
+
this._options.enableHistory = false;
|
|
390
|
+
try {
|
|
391
|
+
switch (action) {
|
|
392
|
+
case 'set':
|
|
393
|
+
if (oldValue === undefined) {
|
|
394
|
+
this.delete(key);
|
|
395
|
+
}
|
|
396
|
+
else {
|
|
397
|
+
this.set(key, oldValue);
|
|
398
|
+
}
|
|
399
|
+
break;
|
|
400
|
+
case 'delete':
|
|
401
|
+
this.set(key, oldValue);
|
|
402
|
+
break;
|
|
403
|
+
case 'clear':
|
|
404
|
+
this.clear();
|
|
405
|
+
Object.entries(oldValue || {}).forEach(([k, v]) => {
|
|
406
|
+
this.set(k, v);
|
|
407
|
+
});
|
|
408
|
+
break;
|
|
409
|
+
}
|
|
410
|
+
return true;
|
|
411
|
+
}
|
|
412
|
+
finally {
|
|
413
|
+
this._options.enableHistory = oldEnableHistory;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Convert state to plain object
|
|
418
|
+
*/
|
|
419
|
+
toObject() {
|
|
420
|
+
const result = {};
|
|
421
|
+
for (const [key, observable] of this._state.entries()) {
|
|
422
|
+
result[key] = observable.value;
|
|
423
|
+
}
|
|
424
|
+
return result;
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Convert computed properties to object
|
|
428
|
+
*/
|
|
429
|
+
getComputedValues() {
|
|
430
|
+
const result = {};
|
|
431
|
+
for (const [key, computed] of this._computed.entries()) {
|
|
432
|
+
result[key] = computed.value;
|
|
433
|
+
}
|
|
434
|
+
return result;
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Get state statistics
|
|
438
|
+
*/
|
|
439
|
+
getStats() {
|
|
440
|
+
return {
|
|
441
|
+
stateKeys: this._state.size,
|
|
442
|
+
computedKeys: this._computed.size,
|
|
443
|
+
watcherKeys: this._watchers.size,
|
|
444
|
+
historyLength: this._history.length,
|
|
445
|
+
middlewareCount: this._middleware.length
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Cleanup and destroy
|
|
450
|
+
*/
|
|
451
|
+
destroy() {
|
|
452
|
+
// Clear all watchers
|
|
453
|
+
for (const observable of this._state.values()) {
|
|
454
|
+
observable.unwatchAll();
|
|
455
|
+
}
|
|
456
|
+
for (const computed of this._computed.values()) {
|
|
457
|
+
computed.unwatchAll();
|
|
458
|
+
}
|
|
459
|
+
// Clear collections
|
|
460
|
+
this._state.clear();
|
|
461
|
+
this._computed.clear();
|
|
462
|
+
this._watchers.clear();
|
|
463
|
+
this._middleware.length = 0;
|
|
464
|
+
this._history.length = 0;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Create reactive state store
|
|
469
|
+
*/
|
|
470
|
+
export function createReactiveState(initialState, options = {}) {
|
|
471
|
+
return new ReactiveState(initialState, options);
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Create observable value
|
|
475
|
+
*/
|
|
476
|
+
export function observable(value, options = {}) {
|
|
477
|
+
return new Observable(value, options);
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Create computed property
|
|
481
|
+
*/
|
|
482
|
+
export function computed(getter, options = {}) {
|
|
483
|
+
return new Computed(getter, options);
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Utility functions for common state patterns
|
|
487
|
+
*/
|
|
488
|
+
export const stateUtils = {
|
|
489
|
+
/**
|
|
490
|
+
* Create a toggle state
|
|
491
|
+
*/
|
|
492
|
+
toggle(initialValue = false) {
|
|
493
|
+
const obs = observable(initialValue);
|
|
494
|
+
obs.toggle = () => {
|
|
495
|
+
obs.value = !obs.value;
|
|
496
|
+
};
|
|
497
|
+
return obs;
|
|
498
|
+
},
|
|
499
|
+
/**
|
|
500
|
+
* Create a counter state
|
|
501
|
+
*/
|
|
502
|
+
counter(initialValue = 0) {
|
|
503
|
+
const obs = observable(initialValue);
|
|
504
|
+
obs.increment = (by = 1) => {
|
|
505
|
+
obs.value += by;
|
|
506
|
+
};
|
|
507
|
+
obs.decrement = (by = 1) => {
|
|
508
|
+
obs.value -= by;
|
|
509
|
+
};
|
|
510
|
+
obs.reset = () => {
|
|
511
|
+
obs.value = initialValue;
|
|
512
|
+
};
|
|
513
|
+
return obs;
|
|
514
|
+
},
|
|
515
|
+
/**
|
|
516
|
+
* Create an array state with utilities
|
|
517
|
+
*/
|
|
518
|
+
array(initialArray = []) {
|
|
519
|
+
const obs = observable([...initialArray]);
|
|
520
|
+
obs.push = (...items) => {
|
|
521
|
+
obs.value = [...obs.value, ...items];
|
|
522
|
+
};
|
|
523
|
+
obs.pop = () => {
|
|
524
|
+
const newArray = [...obs.value];
|
|
525
|
+
const result = newArray.pop();
|
|
526
|
+
obs.value = newArray;
|
|
527
|
+
return result;
|
|
528
|
+
};
|
|
529
|
+
obs.filter = (predicate) => {
|
|
530
|
+
obs.value = obs.value.filter(predicate);
|
|
531
|
+
};
|
|
532
|
+
obs.clear = () => {
|
|
533
|
+
obs.value = [];
|
|
534
|
+
};
|
|
535
|
+
return obs;
|
|
536
|
+
},
|
|
537
|
+
/**
|
|
538
|
+
* Create object state with deep reactivity
|
|
539
|
+
*/
|
|
540
|
+
object(initialObject = {}) {
|
|
541
|
+
const state = createReactiveState(initialObject, { deep: true });
|
|
542
|
+
return state;
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
export default ReactiveState;
|
|
546
|
+
//# sourceMappingURL=reactive-state.js.map
|