@sc4rfurryx/proteusjs 1.0.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.
Files changed (82) hide show
  1. package/API.md +438 -0
  2. package/FEATURES.md +286 -0
  3. package/LICENSE +21 -0
  4. package/README.md +645 -0
  5. package/dist/.tsbuildinfo +1 -0
  6. package/dist/proteus.cjs.js +16014 -0
  7. package/dist/proteus.cjs.js.map +1 -0
  8. package/dist/proteus.d.ts +3018 -0
  9. package/dist/proteus.esm.js +16005 -0
  10. package/dist/proteus.esm.js.map +1 -0
  11. package/dist/proteus.esm.min.js +8 -0
  12. package/dist/proteus.esm.min.js.map +1 -0
  13. package/dist/proteus.js +16020 -0
  14. package/dist/proteus.js.map +1 -0
  15. package/dist/proteus.min.js +8 -0
  16. package/dist/proteus.min.js.map +1 -0
  17. package/package.json +98 -0
  18. package/src/__tests__/mvp-integration.test.ts +518 -0
  19. package/src/accessibility/AccessibilityEngine.ts +2106 -0
  20. package/src/accessibility/ScreenReaderSupport.ts +444 -0
  21. package/src/accessibility/__tests__/ScreenReaderSupport.test.ts +435 -0
  22. package/src/animations/FLIPAnimationSystem.ts +491 -0
  23. package/src/compatibility/BrowserCompatibility.ts +1076 -0
  24. package/src/containers/BreakpointSystem.ts +347 -0
  25. package/src/containers/ContainerBreakpoints.ts +726 -0
  26. package/src/containers/ContainerManager.ts +370 -0
  27. package/src/containers/ContainerUnits.ts +336 -0
  28. package/src/containers/ContextIsolation.ts +394 -0
  29. package/src/containers/ElementQueries.ts +411 -0
  30. package/src/containers/SmartContainer.ts +536 -0
  31. package/src/containers/SmartContainers.ts +376 -0
  32. package/src/containers/__tests__/ContainerBreakpoints.test.ts +411 -0
  33. package/src/containers/__tests__/SmartContainers.test.ts +281 -0
  34. package/src/content/ResponsiveImages.ts +570 -0
  35. package/src/core/EventSystem.ts +147 -0
  36. package/src/core/MemoryManager.ts +321 -0
  37. package/src/core/PerformanceMonitor.ts +238 -0
  38. package/src/core/PluginSystem.ts +275 -0
  39. package/src/core/ProteusJS.test.ts +164 -0
  40. package/src/core/ProteusJS.ts +962 -0
  41. package/src/developer/PerformanceProfiler.ts +567 -0
  42. package/src/developer/VisualDebuggingTools.ts +656 -0
  43. package/src/developer/ZeroConfigSystem.ts +593 -0
  44. package/src/index.ts +35 -0
  45. package/src/integration.test.ts +227 -0
  46. package/src/layout/AdaptiveGrid.ts +429 -0
  47. package/src/layout/ContentReordering.ts +532 -0
  48. package/src/layout/FlexboxEnhancer.ts +406 -0
  49. package/src/layout/FlowLayout.ts +545 -0
  50. package/src/layout/SpacingSystem.ts +512 -0
  51. package/src/observers/IntersectionObserverPolyfill.ts +289 -0
  52. package/src/observers/ObserverManager.ts +299 -0
  53. package/src/observers/ResizeObserverPolyfill.ts +179 -0
  54. package/src/performance/BatchDOMOperations.ts +519 -0
  55. package/src/performance/CSSOptimizationEngine.ts +646 -0
  56. package/src/performance/CacheOptimizationSystem.ts +601 -0
  57. package/src/performance/EfficientEventHandler.ts +740 -0
  58. package/src/performance/LazyEvaluationSystem.ts +532 -0
  59. package/src/performance/MemoryManagementSystem.ts +497 -0
  60. package/src/performance/PerformanceMonitor.ts +931 -0
  61. package/src/performance/__tests__/BatchDOMOperations.test.ts +309 -0
  62. package/src/performance/__tests__/EfficientEventHandler.test.ts +268 -0
  63. package/src/performance/__tests__/PerformanceMonitor.test.ts +422 -0
  64. package/src/polyfills/BrowserPolyfills.ts +586 -0
  65. package/src/polyfills/__tests__/BrowserPolyfills.test.ts +328 -0
  66. package/src/test/setup.ts +115 -0
  67. package/src/theming/SmartThemeSystem.ts +591 -0
  68. package/src/types/index.ts +134 -0
  69. package/src/typography/ClampScaling.ts +356 -0
  70. package/src/typography/FluidTypography.ts +759 -0
  71. package/src/typography/LineHeightOptimization.ts +430 -0
  72. package/src/typography/LineHeightOptimizer.ts +326 -0
  73. package/src/typography/TextFitting.ts +355 -0
  74. package/src/typography/TypographicScale.ts +428 -0
  75. package/src/typography/VerticalRhythm.ts +369 -0
  76. package/src/typography/__tests__/FluidTypography.test.ts +432 -0
  77. package/src/typography/__tests__/LineHeightOptimization.test.ts +436 -0
  78. package/src/utils/Logger.ts +173 -0
  79. package/src/utils/debounce.ts +259 -0
  80. package/src/utils/performance.ts +371 -0
  81. package/src/utils/support.ts +106 -0
  82. package/src/utils/version.ts +24 -0
@@ -0,0 +1,601 @@
1
+ /**
2
+ * Cache Optimization System for ProteusJS
3
+ * Intelligent caching with memory pressure-based eviction
4
+ */
5
+
6
+ export interface CacheConfig {
7
+ maxSize: number;
8
+ maxAge: number;
9
+ memoryThreshold: number;
10
+ evictionStrategy: 'lru' | 'lfu' | 'ttl' | 'adaptive';
11
+ compressionEnabled: boolean;
12
+ persistentStorage: boolean;
13
+ storageKey: string;
14
+ }
15
+
16
+ export interface CacheEntry<T> {
17
+ key: string;
18
+ value: T;
19
+ timestamp: number;
20
+ lastAccessed: number;
21
+ accessCount: number;
22
+ size: number;
23
+ ttl?: number;
24
+ compressed?: boolean;
25
+ }
26
+
27
+ export interface CacheMetrics {
28
+ totalEntries: number;
29
+ totalSize: number;
30
+ hitRate: number;
31
+ missRate: number;
32
+ evictions: number;
33
+ compressionRatio: number;
34
+ }
35
+
36
+ export class CacheOptimizationSystem<T = any> {
37
+ private config: Required<CacheConfig>;
38
+ private cache: Map<string, CacheEntry<T>> = new Map();
39
+ private accessOrder: string[] = [];
40
+ private metrics: CacheMetrics;
41
+ private cleanupTimer: number | null = null;
42
+
43
+ constructor(config: Partial<CacheConfig> = {}) {
44
+ this.config = {
45
+ maxSize: 100,
46
+ maxAge: 300000, // 5 minutes
47
+ memoryThreshold: 10 * 1024 * 1024, // 10MB
48
+ evictionStrategy: 'adaptive',
49
+ compressionEnabled: true,
50
+ persistentStorage: false,
51
+ storageKey: 'proteus-cache',
52
+ ...config
53
+ };
54
+
55
+ this.metrics = this.createInitialMetrics();
56
+ this.loadFromStorage();
57
+ this.startCleanupTimer();
58
+ }
59
+
60
+ /**
61
+ * Get value from cache
62
+ */
63
+ public get(key: string): T | null {
64
+ const entry = this.cache.get(key);
65
+
66
+ if (!entry) {
67
+ this.metrics.missRate++;
68
+ return null;
69
+ }
70
+
71
+ // Check TTL
72
+ if (entry.ttl && Date.now() > entry.timestamp + entry.ttl) {
73
+ this.delete(key);
74
+ this.metrics.missRate++;
75
+ return null;
76
+ }
77
+
78
+ // Update access info
79
+ entry.lastAccessed = Date.now();
80
+ entry.accessCount++;
81
+ this.updateAccessOrder(key);
82
+
83
+ this.metrics.hitRate++;
84
+
85
+ // Decompress if needed
86
+ let value = entry.value;
87
+ if (entry.compressed && this.config.compressionEnabled) {
88
+ value = this.decompress(value);
89
+ }
90
+
91
+ return value;
92
+ }
93
+
94
+ /**
95
+ * Set value in cache
96
+ */
97
+ public set(key: string, value: T, ttl?: number): void {
98
+ // Check if we need to evict entries
99
+ if (this.cache.size >= this.config.maxSize) {
100
+ this.evictEntries(1);
101
+ }
102
+
103
+ // Compress value if enabled
104
+ let finalValue = value;
105
+ let compressed = false;
106
+ if (this.config.compressionEnabled && this.shouldCompress(value)) {
107
+ finalValue = this.compress(value);
108
+ compressed = true;
109
+ }
110
+
111
+ const size = this.calculateSize(finalValue);
112
+ const entry: CacheEntry<T> = {
113
+ key,
114
+ value: finalValue,
115
+ timestamp: Date.now(),
116
+ lastAccessed: Date.now(),
117
+ accessCount: 1,
118
+ size,
119
+ ...(ttl !== undefined && { ttl }),
120
+ compressed
121
+ };
122
+
123
+ // Remove existing entry if it exists
124
+ if (this.cache.has(key)) {
125
+ this.delete(key);
126
+ }
127
+
128
+ this.cache.set(key, entry);
129
+ this.updateAccessOrder(key);
130
+ this.updateMetrics();
131
+
132
+ // Check memory pressure
133
+ this.checkMemoryPressure();
134
+ }
135
+
136
+ /**
137
+ * Delete entry from cache
138
+ */
139
+ public delete(key: string): boolean {
140
+ const deleted = this.cache.delete(key);
141
+ if (deleted) {
142
+ this.removeFromAccessOrder(key);
143
+ this.updateMetrics();
144
+ }
145
+ return deleted;
146
+ }
147
+
148
+ /**
149
+ * Check if key exists in cache
150
+ */
151
+ public has(key: string): boolean {
152
+ const entry = this.cache.get(key);
153
+ if (!entry) return false;
154
+
155
+ // Check TTL
156
+ if (entry.ttl && Date.now() > entry.timestamp + entry.ttl) {
157
+ this.delete(key);
158
+ return false;
159
+ }
160
+
161
+ return true;
162
+ }
163
+
164
+ /**
165
+ * Clear all cache entries
166
+ */
167
+ public clear(): void {
168
+ this.cache.clear();
169
+ this.accessOrder = [];
170
+ this.updateMetrics();
171
+ }
172
+
173
+ /**
174
+ * Get cache metrics
175
+ */
176
+ public getMetrics(): CacheMetrics {
177
+ return { ...this.metrics };
178
+ }
179
+
180
+ /**
181
+ * Cache calculation result with automatic key generation
182
+ */
183
+ public cacheCalculation<R>(
184
+ fn: () => R,
185
+ keyParts: (string | number)[],
186
+ ttl?: number
187
+ ): R {
188
+ const key = this.generateKey('calc', keyParts);
189
+
190
+ let result = this.get(key) as R;
191
+ if (result === null) {
192
+ result = fn();
193
+ this.set(key, result as unknown as T, ttl);
194
+ }
195
+
196
+ return result;
197
+ }
198
+
199
+ /**
200
+ * Cache layout measurement
201
+ */
202
+ public cacheLayoutMeasurement(
203
+ element: Element,
204
+ measurement: () => DOMRect | number,
205
+ ttl: number = 1000 // Short TTL for layout measurements
206
+ ): DOMRect | number {
207
+ const key = this.generateKey('layout', [
208
+ element.tagName,
209
+ element.className,
210
+ element.id || 'no-id'
211
+ ]);
212
+
213
+ let result = this.get(key) as DOMRect | number;
214
+ if (result === null) {
215
+ result = measurement();
216
+ this.set(key, result as unknown as T, ttl);
217
+ }
218
+
219
+ return result;
220
+ }
221
+
222
+ /**
223
+ * Cache container state
224
+ */
225
+ public cacheContainerState(
226
+ containerId: string,
227
+ state: any,
228
+ ttl: number = 5000
229
+ ): void {
230
+ const key = this.generateKey('container', [containerId]);
231
+ this.set(key, state, ttl);
232
+ }
233
+
234
+ /**
235
+ * Get cached container state
236
+ */
237
+ public getCachedContainerState(containerId: string): any {
238
+ const key = this.generateKey('container', [containerId]);
239
+ return this.get(key);
240
+ }
241
+
242
+ /**
243
+ * Cache computed styles
244
+ */
245
+ public cacheComputedStyles(
246
+ element: Element,
247
+ properties: string[],
248
+ ttl: number = 2000
249
+ ): CSSStyleDeclaration | null {
250
+ const key = this.generateKey('styles', [
251
+ element.tagName,
252
+ element.className,
253
+ properties.join(',')
254
+ ]);
255
+
256
+ let styles = this.get(key) as CSSStyleDeclaration;
257
+ if (styles === null) {
258
+ styles = window.getComputedStyle(element);
259
+ // Create a plain object to avoid caching live CSSStyleDeclaration
260
+ const stylesObj: Record<string, string> = {};
261
+ properties.forEach(prop => {
262
+ stylesObj[prop] = styles.getPropertyValue(prop);
263
+ });
264
+ this.set(key, stylesObj as any, ttl);
265
+ return stylesObj as any;
266
+ }
267
+
268
+ return styles;
269
+ }
270
+
271
+ /**
272
+ * Optimize cache based on memory pressure
273
+ */
274
+ public optimize(): void {
275
+ // Remove expired entries
276
+ this.removeExpiredEntries();
277
+
278
+ // Check memory pressure and evict if needed
279
+ this.checkMemoryPressure();
280
+
281
+ // Compress large entries
282
+ this.compressLargeEntries();
283
+
284
+ // Update metrics
285
+ this.updateMetrics();
286
+ }
287
+
288
+ /**
289
+ * Destroy cache system
290
+ */
291
+ public destroy(): void {
292
+ this.stopCleanupTimer();
293
+ this.saveToStorage();
294
+ this.clear();
295
+ }
296
+
297
+ /**
298
+ * Evict entries based on strategy
299
+ */
300
+ private evictEntries(count: number): void {
301
+ const strategy = this.config.evictionStrategy;
302
+ let keysToEvict: string[] = [];
303
+
304
+ switch (strategy) {
305
+ case 'lru':
306
+ keysToEvict = this.getLRUKeys(count);
307
+ break;
308
+ case 'lfu':
309
+ keysToEvict = this.getLFUKeys(count);
310
+ break;
311
+ case 'ttl':
312
+ keysToEvict = this.getTTLKeys(count);
313
+ break;
314
+ case 'adaptive':
315
+ keysToEvict = this.getAdaptiveKeys(count);
316
+ break;
317
+ }
318
+
319
+ keysToEvict.forEach(key => {
320
+ this.delete(key);
321
+ this.metrics.evictions++;
322
+ });
323
+ }
324
+
325
+ /**
326
+ * Get LRU (Least Recently Used) keys
327
+ */
328
+ private getLRUKeys(count: number): string[] {
329
+ return this.accessOrder.slice(0, count);
330
+ }
331
+
332
+ /**
333
+ * Get LFU (Least Frequently Used) keys
334
+ */
335
+ private getLFUKeys(count: number): string[] {
336
+ const entries = Array.from(this.cache.entries());
337
+ entries.sort((a, b) => a[1].accessCount - b[1].accessCount);
338
+ return entries.slice(0, count).map(([key]) => key);
339
+ }
340
+
341
+ /**
342
+ * Get TTL-based keys (oldest first)
343
+ */
344
+ private getTTLKeys(count: number): string[] {
345
+ const entries = Array.from(this.cache.entries());
346
+ entries.sort((a, b) => a[1].timestamp - b[1].timestamp);
347
+ return entries.slice(0, count).map(([key]) => key);
348
+ }
349
+
350
+ /**
351
+ * Get adaptive eviction keys (combination of strategies)
352
+ */
353
+ private getAdaptiveKeys(count: number): string[] {
354
+ const entries = Array.from(this.cache.entries());
355
+
356
+ // Score based on age, access frequency, and size
357
+ entries.forEach(([key, entry]) => {
358
+ const age = Date.now() - entry.lastAccessed;
359
+ const frequency = entry.accessCount;
360
+ const size = entry.size;
361
+
362
+ // Lower score = higher priority for eviction
363
+ (entry as any).score = (age / 1000) + (size / 1024) - (frequency * 10);
364
+ });
365
+
366
+ entries.sort((a, b) => (b[1] as any).score - (a[1] as any).score);
367
+ return entries.slice(0, count).map(([key]) => key);
368
+ }
369
+
370
+ /**
371
+ * Check memory pressure and evict if needed
372
+ */
373
+ private checkMemoryPressure(): void {
374
+ const totalSize = this.getTotalSize();
375
+
376
+ if (totalSize > this.config.memoryThreshold) {
377
+ const evictCount = Math.ceil(this.cache.size * 0.2); // Evict 20%
378
+ this.evictEntries(evictCount);
379
+ }
380
+ }
381
+
382
+ /**
383
+ * Remove expired entries
384
+ */
385
+ private removeExpiredEntries(): void {
386
+ const now = Date.now();
387
+ const expiredKeys: string[] = [];
388
+
389
+ this.cache.forEach((entry, key) => {
390
+ const isExpired = entry.ttl
391
+ ? now > entry.timestamp + entry.ttl
392
+ : now > entry.timestamp + this.config.maxAge;
393
+
394
+ if (isExpired) {
395
+ expiredKeys.push(key);
396
+ }
397
+ });
398
+
399
+ expiredKeys.forEach(key => this.delete(key));
400
+ }
401
+
402
+ /**
403
+ * Compress large entries
404
+ */
405
+ private compressLargeEntries(): void {
406
+ if (!this.config.compressionEnabled) return;
407
+
408
+ this.cache.forEach((entry, key) => {
409
+ if (!entry.compressed && entry.size > 1024) { // 1KB threshold
410
+ const compressed = this.compress(entry.value);
411
+ const compressedSize = this.calculateSize(compressed);
412
+
413
+ if (compressedSize < entry.size * 0.8) { // Only if 20% reduction
414
+ entry.value = compressed;
415
+ entry.size = compressedSize;
416
+ entry.compressed = true;
417
+ }
418
+ }
419
+ });
420
+ }
421
+
422
+ /**
423
+ * Update access order for LRU
424
+ */
425
+ private updateAccessOrder(key: string): void {
426
+ this.removeFromAccessOrder(key);
427
+ this.accessOrder.push(key);
428
+ }
429
+
430
+ /**
431
+ * Remove key from access order
432
+ */
433
+ private removeFromAccessOrder(key: string): void {
434
+ const index = this.accessOrder.indexOf(key);
435
+ if (index > -1) {
436
+ this.accessOrder.splice(index, 1);
437
+ }
438
+ }
439
+
440
+ /**
441
+ * Generate cache key
442
+ */
443
+ private generateKey(prefix: string, parts: (string | number)[]): string {
444
+ return `${prefix}:${parts.join(':')}`;
445
+ }
446
+
447
+ /**
448
+ * Calculate size of value
449
+ */
450
+ private calculateSize(value: any): number {
451
+ if (typeof value === 'string') {
452
+ return value.length * 2; // Approximate UTF-16 size
453
+ }
454
+
455
+ try {
456
+ return JSON.stringify(value).length * 2;
457
+ } catch {
458
+ return 1024; // Default estimate
459
+ }
460
+ }
461
+
462
+ /**
463
+ * Get total cache size
464
+ */
465
+ private getTotalSize(): number {
466
+ let total = 0;
467
+ this.cache.forEach(entry => {
468
+ total += entry.size;
469
+ });
470
+ return total;
471
+ }
472
+
473
+ /**
474
+ * Check if value should be compressed
475
+ */
476
+ private shouldCompress(value: any): boolean {
477
+ const size = this.calculateSize(value);
478
+ return size > 512; // Compress values larger than 512 bytes
479
+ }
480
+
481
+ /**
482
+ * Compress value (simple implementation)
483
+ */
484
+ private compress(value: any): any {
485
+ // In a real implementation, you might use a compression library
486
+ // For now, we'll just stringify and indicate it's compressed
487
+ return {
488
+ __compressed: true,
489
+ data: JSON.stringify(value)
490
+ };
491
+ }
492
+
493
+ /**
494
+ * Decompress value
495
+ */
496
+ private decompress(value: any): any {
497
+ if (value && value.__compressed) {
498
+ return JSON.parse(value.data);
499
+ }
500
+ return value;
501
+ }
502
+
503
+ /**
504
+ * Update cache metrics
505
+ */
506
+ private updateMetrics(): void {
507
+ this.metrics.totalEntries = this.cache.size;
508
+ this.metrics.totalSize = this.getTotalSize();
509
+
510
+ const totalRequests = this.metrics.hitRate + this.metrics.missRate;
511
+ if (totalRequests > 0) {
512
+ this.metrics.hitRate = this.metrics.hitRate / totalRequests;
513
+ this.metrics.missRate = this.metrics.missRate / totalRequests;
514
+ }
515
+
516
+ // Calculate compression ratio
517
+ let totalOriginalSize = 0;
518
+ let totalCompressedSize = 0;
519
+ let compressedCount = 0;
520
+
521
+ this.cache.forEach(entry => {
522
+ if (entry.compressed) {
523
+ compressedCount++;
524
+ totalCompressedSize += entry.size;
525
+ // Estimate original size (compressed data is typically 30-70% of original)
526
+ totalOriginalSize += entry.size / 0.5; // Assume 50% compression
527
+ }
528
+ });
529
+
530
+ if (compressedCount > 0 && totalOriginalSize > 0) {
531
+ this.metrics.compressionRatio = totalCompressedSize / totalOriginalSize;
532
+ }
533
+ }
534
+
535
+ /**
536
+ * Start cleanup timer
537
+ */
538
+ private startCleanupTimer(): void {
539
+ this.cleanupTimer = window.setInterval(() => {
540
+ this.optimize();
541
+ }, 30000); // Cleanup every 30 seconds
542
+ }
543
+
544
+ /**
545
+ * Stop cleanup timer
546
+ */
547
+ private stopCleanupTimer(): void {
548
+ if (this.cleanupTimer) {
549
+ clearInterval(this.cleanupTimer);
550
+ this.cleanupTimer = null;
551
+ }
552
+ }
553
+
554
+ /**
555
+ * Load cache from persistent storage
556
+ */
557
+ private loadFromStorage(): void {
558
+ if (!this.config.persistentStorage) return;
559
+
560
+ try {
561
+ const stored = localStorage.getItem(this.config.storageKey);
562
+ if (stored) {
563
+ const data = JSON.parse(stored);
564
+ data.forEach((entry: CacheEntry<T>) => {
565
+ this.cache.set(entry.key, entry);
566
+ this.accessOrder.push(entry.key);
567
+ });
568
+ }
569
+ } catch (error) {
570
+ console.warn('Failed to load cache from storage:', error);
571
+ }
572
+ }
573
+
574
+ /**
575
+ * Save cache to persistent storage
576
+ */
577
+ private saveToStorage(): void {
578
+ if (!this.config.persistentStorage) return;
579
+
580
+ try {
581
+ const data = Array.from(this.cache.values());
582
+ localStorage.setItem(this.config.storageKey, JSON.stringify(data));
583
+ } catch (error) {
584
+ console.warn('Failed to save cache to storage:', error);
585
+ }
586
+ }
587
+
588
+ /**
589
+ * Create initial metrics
590
+ */
591
+ private createInitialMetrics(): CacheMetrics {
592
+ return {
593
+ totalEntries: 0,
594
+ totalSize: 0,
595
+ hitRate: 0,
596
+ missRate: 0,
597
+ evictions: 0,
598
+ compressionRatio: 0
599
+ };
600
+ }
601
+ }