@holoscript/core 2.0.0 → 2.0.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/dist/chunk-3N67RLQP.cjs +1298 -0
- package/dist/chunk-3N67RLQP.cjs.map +1 -0
- package/dist/chunk-3X2EGU7Z.cjs +52 -0
- package/dist/chunk-3X2EGU7Z.cjs.map +1 -0
- package/dist/chunk-4CV4JOE5.js +24 -0
- package/dist/chunk-4CV4JOE5.js.map +1 -0
- package/dist/chunk-4OHVW4XR.cjs +1027 -0
- package/dist/chunk-4OHVW4XR.cjs.map +1 -0
- package/dist/chunk-CZLDE2OZ.cjs +28 -0
- package/dist/chunk-CZLDE2OZ.cjs.map +1 -0
- package/{src/HoloScriptRuntime.ts → dist/chunk-EU6CZMGJ.js} +437 -794
- package/dist/chunk-EU6CZMGJ.js.map +1 -0
- package/dist/chunk-KWYIVRIH.js +344 -0
- package/dist/chunk-KWYIVRIH.js.map +1 -0
- package/dist/chunk-MCP6D4LT.js +1025 -0
- package/dist/chunk-MCP6D4LT.js.map +1 -0
- package/dist/chunk-SATNCODL.js +45 -0
- package/dist/chunk-SATNCODL.js.map +1 -0
- package/dist/chunk-VMZN4EVR.cjs +347 -0
- package/dist/chunk-VMZN4EVR.cjs.map +1 -0
- package/{src/HoloScriptDebugger.ts → dist/chunk-VYIDLUCV.js} +118 -257
- package/dist/chunk-VYIDLUCV.js.map +1 -0
- package/dist/chunk-WFI4T3XB.cjs +424 -0
- package/dist/chunk-WFI4T3XB.cjs.map +1 -0
- package/dist/debugger.cjs +20 -0
- package/dist/debugger.cjs.map +1 -0
- package/dist/debugger.d.cts +171 -0
- package/dist/debugger.d.ts +171 -0
- package/dist/debugger.js +7 -0
- package/dist/debugger.js.map +1 -0
- package/dist/index.cjs +6006 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2482 -0
- package/dist/index.d.ts +2482 -0
- package/dist/index.js +5926 -0
- package/dist/index.js.map +1 -0
- package/dist/parser.cjs +14 -0
- package/dist/parser.cjs.map +1 -0
- package/dist/parser.d.cts +139 -0
- package/dist/parser.d.ts +139 -0
- package/dist/parser.js +5 -0
- package/dist/parser.js.map +1 -0
- package/dist/runtime.cjs +14 -0
- package/dist/runtime.cjs.map +1 -0
- package/dist/runtime.d.cts +180 -0
- package/dist/runtime.d.ts +180 -0
- package/dist/runtime.js +5 -0
- package/dist/runtime.js.map +1 -0
- package/dist/type-checker.cjs +17 -0
- package/dist/type-checker.cjs.map +1 -0
- package/dist/type-checker.d.cts +105 -0
- package/dist/type-checker.d.ts +105 -0
- package/dist/type-checker.js +4 -0
- package/dist/type-checker.js.map +1 -0
- package/dist/types-D6g4ACjP.d.cts +262 -0
- package/dist/types-D6g4ACjP.d.ts +262 -0
- package/package.json +11 -8
- package/src/HoloScript2DParser.js +0 -227
- package/src/HoloScript2DParser.ts +0 -261
- package/src/HoloScriptCodeParser.js +0 -1102
- package/src/HoloScriptCodeParser.ts +0 -1188
- package/src/HoloScriptDebugger.js +0 -458
- package/src/HoloScriptParser.js +0 -338
- package/src/HoloScriptParser.ts +0 -397
- package/src/HoloScriptPlusParser.js +0 -371
- package/src/HoloScriptPlusParser.ts +0 -543
- package/src/HoloScriptRuntime.js +0 -1399
- package/src/HoloScriptRuntime.test.js +0 -351
- package/src/HoloScriptRuntime.test.ts +0 -436
- package/src/HoloScriptTypeChecker.js +0 -356
- package/src/HoloScriptTypeChecker.ts +0 -475
- package/src/__tests__/GraphicsServices.test.js +0 -357
- package/src/__tests__/GraphicsServices.test.ts +0 -427
- package/src/__tests__/HoloScriptPlusParser.test.js +0 -317
- package/src/__tests__/HoloScriptPlusParser.test.ts +0 -392
- package/src/__tests__/integration.test.js +0 -336
- package/src/__tests__/integration.test.ts +0 -416
- package/src/__tests__/performance.bench.js +0 -218
- package/src/__tests__/performance.bench.ts +0 -262
- package/src/__tests__/type-checker.test.js +0 -60
- package/src/__tests__/type-checker.test.ts +0 -73
- package/src/index.js +0 -217
- package/src/index.ts +0 -426
- package/src/interop/Interoperability.js +0 -413
- package/src/interop/Interoperability.ts +0 -494
- package/src/logger.js +0 -42
- package/src/logger.ts +0 -57
- package/src/parser/EnhancedParser.js +0 -205
- package/src/parser/EnhancedParser.ts +0 -251
- package/src/parser/HoloScriptPlusParser.js +0 -928
- package/src/parser/HoloScriptPlusParser.ts +0 -1089
- package/src/runtime/HoloScriptPlusRuntime.js +0 -674
- package/src/runtime/HoloScriptPlusRuntime.ts +0 -861
- package/src/runtime/PerformanceTelemetry.js +0 -323
- package/src/runtime/PerformanceTelemetry.ts +0 -467
- package/src/runtime/RuntimeOptimization.js +0 -361
- package/src/runtime/RuntimeOptimization.ts +0 -416
- package/src/services/HololandGraphicsPipelineService.js +0 -506
- package/src/services/HololandGraphicsPipelineService.ts +0 -662
- package/src/services/PlatformPerformanceOptimizer.js +0 -356
- package/src/services/PlatformPerformanceOptimizer.ts +0 -503
- package/src/state/ReactiveState.js +0 -427
- package/src/state/ReactiveState.ts +0 -572
- package/src/tools/DeveloperExperience.js +0 -376
- package/src/tools/DeveloperExperience.ts +0 -438
- package/src/traits/AIDriverTrait.js +0 -322
- package/src/traits/AIDriverTrait.test.js +0 -329
- package/src/traits/AIDriverTrait.test.ts +0 -357
- package/src/traits/AIDriverTrait.ts +0 -474
- package/src/traits/LightingTrait.js +0 -313
- package/src/traits/LightingTrait.test.js +0 -410
- package/src/traits/LightingTrait.test.ts +0 -462
- package/src/traits/LightingTrait.ts +0 -505
- package/src/traits/MaterialTrait.js +0 -194
- package/src/traits/MaterialTrait.test.js +0 -286
- package/src/traits/MaterialTrait.test.ts +0 -329
- package/src/traits/MaterialTrait.ts +0 -324
- package/src/traits/RenderingTrait.js +0 -356
- package/src/traits/RenderingTrait.test.js +0 -363
- package/src/traits/RenderingTrait.test.ts +0 -427
- package/src/traits/RenderingTrait.ts +0 -555
- package/src/traits/VRTraitSystem.js +0 -740
- package/src/traits/VRTraitSystem.ts +0 -1040
- package/src/traits/VoiceInputTrait.js +0 -284
- package/src/traits/VoiceInputTrait.test.js +0 -226
- package/src/traits/VoiceInputTrait.test.ts +0 -252
- package/src/traits/VoiceInputTrait.ts +0 -401
- package/src/types/AdvancedTypeSystem.js +0 -226
- package/src/types/AdvancedTypeSystem.ts +0 -494
- package/src/types/HoloScriptPlus.d.ts +0 -853
- package/src/types.js +0 -6
- package/src/types.ts +0 -369
- package/tsconfig.json +0 -23
- package/tsup.config.d.ts +0 -2
- package/tsup.config.js +0 -18
- package/tsup.config.ts +0 -19
|
@@ -1,572 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reactive State System for HoloScript+
|
|
3
|
-
*
|
|
4
|
-
* Provides reactive state management with:
|
|
5
|
-
* - Proxy-based reactivity
|
|
6
|
-
* - Computed properties
|
|
7
|
-
* - Effect system for side effects
|
|
8
|
-
* - Batched updates for performance
|
|
9
|
-
*
|
|
10
|
-
* @version 1.0.0
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import type { StateDeclaration, ReactiveState as IReactiveState } from '../types/HoloScriptPlus';
|
|
14
|
-
|
|
15
|
-
// =============================================================================
|
|
16
|
-
// TYPES
|
|
17
|
-
// =============================================================================
|
|
18
|
-
|
|
19
|
-
type Subscriber<T> = (state: T, changedKey?: keyof T) => void;
|
|
20
|
-
type UnsubscribeFunc = () => void;
|
|
21
|
-
type EffectFunc = () => void | (() => void);
|
|
22
|
-
type ComputedFunc<T> = () => T;
|
|
23
|
-
|
|
24
|
-
interface EffectOptions {
|
|
25
|
-
immediate?: boolean;
|
|
26
|
-
deep?: boolean;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// interface WatchOptions<T> extends EffectOptions {
|
|
30
|
-
// handler: (newValue: T, oldValue: T) => void;
|
|
31
|
-
// }
|
|
32
|
-
|
|
33
|
-
// =============================================================================
|
|
34
|
-
// DEPENDENCY TRACKING
|
|
35
|
-
// =============================================================================
|
|
36
|
-
|
|
37
|
-
let activeEffect: EffectFunc | null = null;
|
|
38
|
-
const targetMap = new WeakMap<object, Map<string | symbol, Set<EffectFunc>>>();
|
|
39
|
-
|
|
40
|
-
function track(target: object, key: string | symbol): void {
|
|
41
|
-
if (!activeEffect) return;
|
|
42
|
-
|
|
43
|
-
let depsMap = targetMap.get(target);
|
|
44
|
-
if (!depsMap) {
|
|
45
|
-
depsMap = new Map();
|
|
46
|
-
targetMap.set(target, depsMap);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
let dep = depsMap.get(key);
|
|
50
|
-
if (!dep) {
|
|
51
|
-
dep = new Set();
|
|
52
|
-
depsMap.set(key, dep);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
dep.add(activeEffect);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function trigger(target: object, key: string | symbol): void {
|
|
59
|
-
const depsMap = targetMap.get(target);
|
|
60
|
-
if (!depsMap) return;
|
|
61
|
-
|
|
62
|
-
const dep = depsMap.get(key);
|
|
63
|
-
if (!dep) return;
|
|
64
|
-
|
|
65
|
-
// Create a copy to avoid infinite loops if effects modify dependencies
|
|
66
|
-
const effectsToRun = new Set(dep);
|
|
67
|
-
effectsToRun.forEach((effect) => {
|
|
68
|
-
// Avoid running effect if it's the active one
|
|
69
|
-
if (effect !== activeEffect) {
|
|
70
|
-
queueEffect(effect);
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// =============================================================================
|
|
76
|
-
// EFFECT BATCHING
|
|
77
|
-
// =============================================================================
|
|
78
|
-
|
|
79
|
-
const pendingEffects = new Set<EffectFunc>();
|
|
80
|
-
let isFlushing = false;
|
|
81
|
-
|
|
82
|
-
function queueEffect(effect: EffectFunc): void {
|
|
83
|
-
pendingEffects.add(effect);
|
|
84
|
-
if (!isFlushing) {
|
|
85
|
-
isFlushing = true;
|
|
86
|
-
Promise.resolve().then(flushEffects);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function flushEffects(): void {
|
|
91
|
-
pendingEffects.forEach((effect) => {
|
|
92
|
-
try {
|
|
93
|
-
runEffect(effect);
|
|
94
|
-
} catch (error) {
|
|
95
|
-
console.error('Error running effect:', error);
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
pendingEffects.clear();
|
|
99
|
-
isFlushing = false;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function runEffect(effect: EffectFunc): void {
|
|
103
|
-
const prevEffect = activeEffect;
|
|
104
|
-
activeEffect = effect;
|
|
105
|
-
try {
|
|
106
|
-
effect();
|
|
107
|
-
} finally {
|
|
108
|
-
activeEffect = prevEffect;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// =============================================================================
|
|
113
|
-
// REACTIVE PROXY
|
|
114
|
-
// =============================================================================
|
|
115
|
-
|
|
116
|
-
function createReactiveProxy<T extends object>(target: T): T {
|
|
117
|
-
return new Proxy(target, {
|
|
118
|
-
get(obj, key: string | symbol) {
|
|
119
|
-
track(obj, key);
|
|
120
|
-
const value = Reflect.get(obj, key);
|
|
121
|
-
|
|
122
|
-
// Deep reactivity for nested objects
|
|
123
|
-
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
|
|
124
|
-
return createReactiveProxy(value);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return value;
|
|
128
|
-
},
|
|
129
|
-
|
|
130
|
-
set(obj, key: string | symbol, value: unknown) {
|
|
131
|
-
const oldValue = Reflect.get(obj, key);
|
|
132
|
-
const result = Reflect.set(obj, key, value);
|
|
133
|
-
|
|
134
|
-
if (oldValue !== value) {
|
|
135
|
-
trigger(obj, key);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
return result;
|
|
139
|
-
},
|
|
140
|
-
|
|
141
|
-
deleteProperty(obj, key: string | symbol) {
|
|
142
|
-
const hadKey = Reflect.has(obj, key);
|
|
143
|
-
const result = Reflect.deleteProperty(obj, key);
|
|
144
|
-
|
|
145
|
-
if (hadKey) {
|
|
146
|
-
trigger(obj, key);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return result;
|
|
150
|
-
},
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// =============================================================================
|
|
155
|
-
// REACTIVE STATE CLASS
|
|
156
|
-
// =============================================================================
|
|
157
|
-
|
|
158
|
-
export class ReactiveState<T extends StateDeclaration> implements IReactiveState<T> {
|
|
159
|
-
private state: T;
|
|
160
|
-
private proxy: T;
|
|
161
|
-
private subscribers: Set<Subscriber<T>> = new Set();
|
|
162
|
-
private computedCache: Map<string, { value: unknown; dirty: boolean }> = new Map();
|
|
163
|
-
private watchCleanups: Map<string, () => void> = new Map();
|
|
164
|
-
|
|
165
|
-
constructor(initialState: T) {
|
|
166
|
-
this.state = { ...initialState };
|
|
167
|
-
this.proxy = createReactiveProxy(this.state);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
get<K extends keyof T>(key: K): T[K] {
|
|
171
|
-
return this.proxy[key];
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
set<K extends keyof T>(key: K, value: T[K]): void {
|
|
175
|
-
const oldValue = this.state[key];
|
|
176
|
-
this.proxy[key] = value;
|
|
177
|
-
|
|
178
|
-
if (oldValue !== value) {
|
|
179
|
-
this.notifySubscribers(key);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
update(updates: Partial<T>): void {
|
|
184
|
-
const changedKeys: (keyof T)[] = [];
|
|
185
|
-
|
|
186
|
-
for (const key in updates) {
|
|
187
|
-
if (Object.prototype.hasOwnProperty.call(updates, key)) {
|
|
188
|
-
const oldValue = this.state[key];
|
|
189
|
-
const newValue = updates[key];
|
|
190
|
-
|
|
191
|
-
if (oldValue !== newValue) {
|
|
192
|
-
this.proxy[key] = newValue as T[typeof key];
|
|
193
|
-
changedKeys.push(key);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if (changedKeys.length > 0) {
|
|
199
|
-
// Batch notify for all changes
|
|
200
|
-
this.subscribers.forEach((callback) => {
|
|
201
|
-
callback(this.state);
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
subscribe(callback: Subscriber<T>): UnsubscribeFunc {
|
|
207
|
-
this.subscribers.add(callback);
|
|
208
|
-
return () => {
|
|
209
|
-
this.subscribers.delete(callback);
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
private notifySubscribers(changedKey: keyof T): void {
|
|
214
|
-
this.subscribers.forEach((callback) => {
|
|
215
|
-
callback(this.state, changedKey);
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// ==========================================================================
|
|
220
|
-
// COMPUTED PROPERTIES
|
|
221
|
-
// ==========================================================================
|
|
222
|
-
|
|
223
|
-
computed<R>(key: string, getter: ComputedFunc<R>): R {
|
|
224
|
-
const cached = this.computedCache.get(key);
|
|
225
|
-
|
|
226
|
-
if (cached && !cached.dirty) {
|
|
227
|
-
return cached.value as R;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// Track dependencies
|
|
231
|
-
const prevEffect = activeEffect;
|
|
232
|
-
activeEffect = () => {
|
|
233
|
-
// Mark cached value as dirty
|
|
234
|
-
const entry = this.computedCache.get(key);
|
|
235
|
-
if (entry) {
|
|
236
|
-
entry.dirty = true;
|
|
237
|
-
}
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
try {
|
|
241
|
-
const value = getter();
|
|
242
|
-
this.computedCache.set(key, { value, dirty: false });
|
|
243
|
-
return value;
|
|
244
|
-
} finally {
|
|
245
|
-
activeEffect = prevEffect;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// ==========================================================================
|
|
250
|
-
// WATCHERS
|
|
251
|
-
// ==========================================================================
|
|
252
|
-
|
|
253
|
-
watch<K extends keyof T>(
|
|
254
|
-
key: K,
|
|
255
|
-
handler: (newValue: T[K], oldValue: T[K]) => void,
|
|
256
|
-
options: EffectOptions = {}
|
|
257
|
-
): UnsubscribeFunc {
|
|
258
|
-
let oldValue = this.state[key];
|
|
259
|
-
|
|
260
|
-
const effect = () => {
|
|
261
|
-
const newValue = this.proxy[key];
|
|
262
|
-
if (newValue !== oldValue) {
|
|
263
|
-
handler(newValue, oldValue);
|
|
264
|
-
oldValue = newValue;
|
|
265
|
-
}
|
|
266
|
-
};
|
|
267
|
-
|
|
268
|
-
// Run immediately if requested
|
|
269
|
-
if (options.immediate) {
|
|
270
|
-
handler(this.state[key], undefined as unknown as T[K]);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// Subscribe to changes
|
|
274
|
-
return this.subscribe((_state, changedKey) => {
|
|
275
|
-
if (changedKey === key) {
|
|
276
|
-
effect();
|
|
277
|
-
}
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
watchEffect(effect: EffectFunc, _options: EffectOptions = {}): UnsubscribeFunc {
|
|
282
|
-
let cleanup: (() => void) | void;
|
|
283
|
-
|
|
284
|
-
const wrappedEffect = () => {
|
|
285
|
-
// Run cleanup from previous run
|
|
286
|
-
if (cleanup) {
|
|
287
|
-
cleanup();
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
cleanup = effect();
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
// Run immediately
|
|
294
|
-
runEffect(wrappedEffect);
|
|
295
|
-
|
|
296
|
-
return () => {
|
|
297
|
-
if (cleanup) {
|
|
298
|
-
cleanup();
|
|
299
|
-
}
|
|
300
|
-
// Note: Would need to remove from dependency tracking
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// ==========================================================================
|
|
305
|
-
// SNAPSHOT / RESET
|
|
306
|
-
// ==========================================================================
|
|
307
|
-
|
|
308
|
-
getSnapshot(): T {
|
|
309
|
-
return { ...this.state };
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
reset(newState?: T): void {
|
|
313
|
-
const stateToSet = newState || ({} as T);
|
|
314
|
-
|
|
315
|
-
// Clear all keys
|
|
316
|
-
for (const key in this.state) {
|
|
317
|
-
if (Object.prototype.hasOwnProperty.call(this.state, key)) {
|
|
318
|
-
delete this.state[key];
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// Set new state
|
|
323
|
-
for (const key in stateToSet) {
|
|
324
|
-
if (Object.prototype.hasOwnProperty.call(stateToSet, key)) {
|
|
325
|
-
this.state[key] = stateToSet[key];
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// Notify all subscribers
|
|
330
|
-
this.subscribers.forEach((callback) => {
|
|
331
|
-
callback(this.state);
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// ==========================================================================
|
|
336
|
-
// DESTROY
|
|
337
|
-
// ==========================================================================
|
|
338
|
-
|
|
339
|
-
destroy(): void {
|
|
340
|
-
this.subscribers.clear();
|
|
341
|
-
this.computedCache.clear();
|
|
342
|
-
this.watchCleanups.forEach((cleanup) => cleanup());
|
|
343
|
-
this.watchCleanups.clear();
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
// =============================================================================
|
|
348
|
-
// FACTORY FUNCTIONS
|
|
349
|
-
// =============================================================================
|
|
350
|
-
|
|
351
|
-
export function createState<T extends StateDeclaration>(initialState: T): ReactiveState<T> {
|
|
352
|
-
return new ReactiveState(initialState);
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
export function ref<T>(value: T): { value: T } {
|
|
356
|
-
return createReactiveProxy({ value });
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
export function reactive<T extends object>(target: T): T {
|
|
360
|
-
return createReactiveProxy(target);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
export function effect(fn: EffectFunc, _options?: EffectOptions): UnsubscribeFunc {
|
|
364
|
-
let cleanup: (() => void) | void;
|
|
365
|
-
|
|
366
|
-
const wrappedEffect = () => {
|
|
367
|
-
if (cleanup) {
|
|
368
|
-
cleanup();
|
|
369
|
-
}
|
|
370
|
-
cleanup = fn();
|
|
371
|
-
};
|
|
372
|
-
|
|
373
|
-
runEffect(wrappedEffect);
|
|
374
|
-
|
|
375
|
-
return () => {
|
|
376
|
-
if (cleanup) {
|
|
377
|
-
cleanup();
|
|
378
|
-
}
|
|
379
|
-
};
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
export function computed<T>(getter: ComputedFunc<T>): { readonly value: T } {
|
|
383
|
-
let value: T;
|
|
384
|
-
let dirty = true;
|
|
385
|
-
|
|
386
|
-
const runner = () => {
|
|
387
|
-
dirty = true;
|
|
388
|
-
};
|
|
389
|
-
|
|
390
|
-
return {
|
|
391
|
-
get value(): T {
|
|
392
|
-
if (dirty) {
|
|
393
|
-
const prevEffect = activeEffect;
|
|
394
|
-
activeEffect = runner;
|
|
395
|
-
try {
|
|
396
|
-
value = getter();
|
|
397
|
-
dirty = false;
|
|
398
|
-
} finally {
|
|
399
|
-
activeEffect = prevEffect;
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
return value;
|
|
403
|
-
},
|
|
404
|
-
};
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// =============================================================================
|
|
408
|
-
// STATE BINDING UTILITIES
|
|
409
|
-
// =============================================================================
|
|
410
|
-
|
|
411
|
-
export interface StateBinding<T> {
|
|
412
|
-
get: () => T;
|
|
413
|
-
set: (value: T) => void;
|
|
414
|
-
subscribe: (callback: (value: T) => void) => UnsubscribeFunc;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
export function bind<T extends StateDeclaration, K extends keyof T>(
|
|
418
|
-
state: ReactiveState<T>,
|
|
419
|
-
key: K
|
|
420
|
-
): StateBinding<T[K]> {
|
|
421
|
-
return {
|
|
422
|
-
get: () => state.get(key),
|
|
423
|
-
set: (value: T[K]) => state.set(key, value),
|
|
424
|
-
subscribe: (callback: (value: T[K]) => void) => {
|
|
425
|
-
return state.subscribe((s, changedKey) => {
|
|
426
|
-
if (changedKey === key || changedKey === undefined) {
|
|
427
|
-
callback(s[key]);
|
|
428
|
-
}
|
|
429
|
-
});
|
|
430
|
-
},
|
|
431
|
-
};
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
// =============================================================================
|
|
435
|
-
// EXPRESSION EVALUATOR
|
|
436
|
-
// =============================================================================
|
|
437
|
-
|
|
438
|
-
export class ExpressionEvaluator {
|
|
439
|
-
private context: Record<string, unknown>;
|
|
440
|
-
private builtins: Record<string, unknown>;
|
|
441
|
-
|
|
442
|
-
constructor(context: Record<string, unknown> = {}, builtins: Record<string, unknown> = {}) {
|
|
443
|
-
this.context = context;
|
|
444
|
-
this.builtins = {
|
|
445
|
-
Math,
|
|
446
|
-
parseInt,
|
|
447
|
-
parseFloat,
|
|
448
|
-
String,
|
|
449
|
-
Number,
|
|
450
|
-
Boolean,
|
|
451
|
-
Array,
|
|
452
|
-
Object,
|
|
453
|
-
JSON,
|
|
454
|
-
Date,
|
|
455
|
-
...builtins,
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
evaluate(expression: string): unknown {
|
|
460
|
-
// Security: Create safe evaluation context
|
|
461
|
-
const contextKeys = Object.keys(this.context);
|
|
462
|
-
const contextValues = Object.values(this.context);
|
|
463
|
-
const builtinKeys = Object.keys(this.builtins);
|
|
464
|
-
const builtinValues = Object.values(this.builtins);
|
|
465
|
-
|
|
466
|
-
try {
|
|
467
|
-
// Create function with context variables as parameters
|
|
468
|
-
const fn = new Function(
|
|
469
|
-
...contextKeys,
|
|
470
|
-
...builtinKeys,
|
|
471
|
-
`"use strict"; return (${expression})`
|
|
472
|
-
);
|
|
473
|
-
|
|
474
|
-
return fn(...contextValues, ...builtinValues);
|
|
475
|
-
} catch (error) {
|
|
476
|
-
console.error(`Error evaluating expression: ${expression}`, error);
|
|
477
|
-
return undefined;
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
updateContext(updates: Record<string, unknown>): void {
|
|
482
|
-
Object.assign(this.context, updates);
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
setContext(context: Record<string, unknown>): void {
|
|
486
|
-
this.context = context;
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
// =============================================================================
|
|
491
|
-
// STATE-TO-DOM BINDING (for future renderer)
|
|
492
|
-
// =============================================================================
|
|
493
|
-
|
|
494
|
-
export interface DOMBinding {
|
|
495
|
-
element: unknown;
|
|
496
|
-
property: string;
|
|
497
|
-
expression: string;
|
|
498
|
-
unsubscribe: UnsubscribeFunc;
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
export class StateDOMBinder<T extends StateDeclaration> {
|
|
502
|
-
private state: ReactiveState<T>;
|
|
503
|
-
private evaluator: ExpressionEvaluator;
|
|
504
|
-
private bindings: DOMBinding[] = [];
|
|
505
|
-
|
|
506
|
-
constructor(state: ReactiveState<T>) {
|
|
507
|
-
this.state = state;
|
|
508
|
-
this.evaluator = new ExpressionEvaluator();
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
bind(element: unknown, property: string, expression: string): DOMBinding {
|
|
512
|
-
// Update evaluator context with state
|
|
513
|
-
this.evaluator.setContext(this.state.getSnapshot() as Record<string, unknown>);
|
|
514
|
-
|
|
515
|
-
// Initial evaluation
|
|
516
|
-
const value = this.evaluator.evaluate(expression);
|
|
517
|
-
this.applyValue(element, property, value);
|
|
518
|
-
|
|
519
|
-
// Subscribe to state changes
|
|
520
|
-
const unsubscribe = this.state.subscribe((newState) => {
|
|
521
|
-
this.evaluator.setContext(newState as Record<string, unknown>);
|
|
522
|
-
const newValue = this.evaluator.evaluate(expression);
|
|
523
|
-
this.applyValue(element, property, newValue);
|
|
524
|
-
});
|
|
525
|
-
|
|
526
|
-
const binding: DOMBinding = { element, property, expression, unsubscribe };
|
|
527
|
-
this.bindings.push(binding);
|
|
528
|
-
|
|
529
|
-
return binding;
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
unbind(binding: DOMBinding): void {
|
|
533
|
-
binding.unsubscribe();
|
|
534
|
-
const index = this.bindings.indexOf(binding);
|
|
535
|
-
if (index > -1) {
|
|
536
|
-
this.bindings.splice(index, 1);
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
unbindAll(): void {
|
|
541
|
-
this.bindings.forEach((binding) => binding.unsubscribe());
|
|
542
|
-
this.bindings = [];
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
private applyValue(element: unknown, property: string, value: unknown): void {
|
|
546
|
-
// Abstract - actual implementation depends on renderer (Three.js, DOM, etc.)
|
|
547
|
-
if (element && typeof element === 'object') {
|
|
548
|
-
const el = element as Record<string, unknown>;
|
|
549
|
-
const props = property.split('.');
|
|
550
|
-
let target = el;
|
|
551
|
-
|
|
552
|
-
for (let i = 0; i < props.length - 1; i++) {
|
|
553
|
-
target = target[props[i]] as Record<string, unknown>;
|
|
554
|
-
if (!target) return;
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
target[props[props.length - 1]] = value;
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
// =============================================================================
|
|
563
|
-
// EXPORTS
|
|
564
|
-
// =============================================================================
|
|
565
|
-
|
|
566
|
-
export {
|
|
567
|
-
track,
|
|
568
|
-
trigger,
|
|
569
|
-
runEffect,
|
|
570
|
-
flushEffects,
|
|
571
|
-
createReactiveProxy,
|
|
572
|
-
};
|