@coherent.js/devtools 1.0.0-beta.8 → 1.0.0-rc.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.
@@ -0,0 +1,1443 @@
1
+ // src/performance/code-splitting.js
2
+ var CodeSplitter = class {
3
+ constructor(options = {}) {
4
+ this.options = {
5
+ preload: [],
6
+ prefetch: [],
7
+ timeout: 1e4,
8
+ retries: 3,
9
+ ...options
10
+ };
11
+ this.modules = /* @__PURE__ */ new Map();
12
+ this.loading = /* @__PURE__ */ new Map();
13
+ this.failed = /* @__PURE__ */ new Set();
14
+ }
15
+ /**
16
+ * Dynamically import a module
17
+ *
18
+ * @param {string} path - Module path
19
+ * @param {Object} [options] - Import options
20
+ * @returns {Promise} Module exports
21
+ */
22
+ async import(path, options = {}) {
23
+ if (this.modules.has(path)) {
24
+ return this.modules.get(path);
25
+ }
26
+ if (this.loading.has(path)) {
27
+ return this.loading.get(path);
28
+ }
29
+ const importPromise = this.loadModule(path, options);
30
+ this.loading.set(path, importPromise);
31
+ try {
32
+ const module = await importPromise;
33
+ this.modules.set(path, module);
34
+ this.loading.delete(path);
35
+ return module;
36
+ } catch (error) {
37
+ this.loading.delete(path);
38
+ this.failed.add(path);
39
+ throw error;
40
+ }
41
+ }
42
+ /**
43
+ * Load module with retries
44
+ */
45
+ async loadModule(path, options = {}) {
46
+ const maxRetries = options.retries ?? this.options.retries;
47
+ const timeout = options.timeout ?? this.options.timeout;
48
+ let lastError;
49
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
50
+ try {
51
+ const importPath = attempt > 0 ? `${path}?retry=${attempt}&t=${Date.now()}` : path;
52
+ const module = await Promise.race([
53
+ import(importPath),
54
+ new Promise(
55
+ (_, reject) => setTimeout(() => reject(new Error("Import timeout")), timeout)
56
+ )
57
+ ]);
58
+ return module;
59
+ } catch (error) {
60
+ lastError = error;
61
+ if (attempt < maxRetries) {
62
+ await new Promise(
63
+ (resolve) => setTimeout(resolve, Math.pow(2, attempt) * 1e3)
64
+ );
65
+ }
66
+ }
67
+ }
68
+ throw new Error(`Failed to load module ${path}: ${lastError.message}`);
69
+ }
70
+ /**
71
+ * Preload modules
72
+ */
73
+ async preload(paths) {
74
+ const pathArray = Array.isArray(paths) ? paths : [paths];
75
+ return Promise.all(
76
+ pathArray.map((path) => this.import(path).catch((err) => {
77
+ console.warn(`Failed to preload ${path}:`, err);
78
+ return null;
79
+ }))
80
+ );
81
+ }
82
+ /**
83
+ * Prefetch modules (low priority)
84
+ */
85
+ prefetch(paths) {
86
+ const pathArray = Array.isArray(paths) ? paths : [paths];
87
+ if (typeof requestIdleCallback !== "undefined") {
88
+ requestIdleCallback(() => {
89
+ pathArray.forEach((path) => {
90
+ this.import(path).catch(() => {
91
+ });
92
+ });
93
+ });
94
+ } else {
95
+ setTimeout(() => {
96
+ pathArray.forEach((path) => {
97
+ this.import(path).catch(() => {
98
+ });
99
+ });
100
+ }, 0);
101
+ }
102
+ }
103
+ /**
104
+ * Check if module is loaded
105
+ */
106
+ isLoaded(path) {
107
+ return this.modules.has(path);
108
+ }
109
+ /**
110
+ * Check if module is loading
111
+ */
112
+ isLoading(path) {
113
+ return this.loading.has(path);
114
+ }
115
+ /**
116
+ * Check if module failed to load
117
+ */
118
+ hasFailed(path) {
119
+ return this.failed.has(path);
120
+ }
121
+ /**
122
+ * Clear cache
123
+ */
124
+ clearCache(path = null) {
125
+ if (path) {
126
+ this.modules.delete(path);
127
+ this.failed.delete(path);
128
+ } else {
129
+ this.modules.clear();
130
+ this.failed.clear();
131
+ }
132
+ }
133
+ /**
134
+ * Get statistics
135
+ */
136
+ getStats() {
137
+ return {
138
+ loaded: this.modules.size,
139
+ loading: this.loading.size,
140
+ failed: this.failed.size,
141
+ modules: Array.from(this.modules.keys())
142
+ };
143
+ }
144
+ };
145
+ function createCodeSplitter(options = {}) {
146
+ return new CodeSplitter(options);
147
+ }
148
+ function lazy(loader, options = {}) {
149
+ let modulePromise = null;
150
+ let module = null;
151
+ let error = null;
152
+ return function LazyComponent(props = {}) {
153
+ if (module) {
154
+ const Component = module.default || module;
155
+ return Component(props);
156
+ }
157
+ if (error) {
158
+ if (options.errorComponent) {
159
+ return options.errorComponent({ error, retry: () => {
160
+ error = null;
161
+ modulePromise = null;
162
+ return LazyComponent(props);
163
+ } });
164
+ }
165
+ return {
166
+ div: {
167
+ className: "lazy-error",
168
+ text: `Error loading component: ${error.message}`
169
+ }
170
+ };
171
+ }
172
+ if (!modulePromise) {
173
+ modulePromise = loader().then((mod) => {
174
+ module = mod;
175
+ return mod;
176
+ }).catch((err) => {
177
+ error = err;
178
+ throw err;
179
+ });
180
+ }
181
+ if (options.loadingComponent) {
182
+ return options.loadingComponent(props);
183
+ }
184
+ return {
185
+ div: {
186
+ className: "lazy-loading",
187
+ text: options.loadingText || "Loading..."
188
+ }
189
+ };
190
+ };
191
+ }
192
+ function splitComponent(componentPath, options = {}) {
193
+ const splitter = new CodeSplitter(options);
194
+ return lazy(
195
+ () => splitter.import(componentPath),
196
+ options
197
+ );
198
+ }
199
+ function createRouteSplitter(routes) {
200
+ const splitter = new CodeSplitter();
201
+ const routeMap = /* @__PURE__ */ new Map();
202
+ for (const [path, config] of Object.entries(routes)) {
203
+ if (typeof config === "string") {
204
+ routeMap.set(path, {
205
+ loader: () => splitter.import(config)
206
+ });
207
+ } else {
208
+ routeMap.set(path, {
209
+ loader: () => splitter.import(config.component),
210
+ preload: config.preload || [],
211
+ ...config
212
+ });
213
+ }
214
+ }
215
+ return {
216
+ /**
217
+ * Load route component
218
+ */
219
+ async loadRoute(path) {
220
+ const route = routeMap.get(path);
221
+ if (!route) {
222
+ throw new Error(`Route not found: ${path}`);
223
+ }
224
+ if (route.preload && route.preload.length > 0) {
225
+ splitter.prefetch(route.preload);
226
+ }
227
+ return await route.loader();
228
+ },
229
+ /**
230
+ * Preload route
231
+ */
232
+ preloadRoute(path) {
233
+ const route = routeMap.get(path);
234
+ if (route) {
235
+ return route.loader();
236
+ }
237
+ },
238
+ /**
239
+ * Get all routes
240
+ */
241
+ getRoutes() {
242
+ return Array.from(routeMap.keys());
243
+ },
244
+ /**
245
+ * Get splitter instance
246
+ */
247
+ getSplitter() {
248
+ return splitter;
249
+ }
250
+ };
251
+ }
252
+ var BundleAnalyzer = class {
253
+ constructor() {
254
+ this.chunks = /* @__PURE__ */ new Map();
255
+ this.loadTimes = /* @__PURE__ */ new Map();
256
+ }
257
+ /**
258
+ * Track chunk load
259
+ */
260
+ trackLoad(chunkName, size, loadTime) {
261
+ this.chunks.set(chunkName, { size, loadTime });
262
+ this.loadTimes.set(chunkName, loadTime);
263
+ }
264
+ /**
265
+ * Get bundle statistics
266
+ */
267
+ getStats() {
268
+ const chunks = Array.from(this.chunks.entries());
269
+ const totalSize = chunks.reduce((sum, [, chunk]) => sum + chunk.size, 0);
270
+ const avgLoadTime = chunks.reduce((sum, [, chunk]) => sum + chunk.loadTime, 0) / chunks.length;
271
+ return {
272
+ totalChunks: chunks.length,
273
+ totalSize,
274
+ averageLoadTime: avgLoadTime,
275
+ chunks: chunks.map(([name, data]) => ({
276
+ name,
277
+ size: data.size,
278
+ loadTime: data.loadTime,
279
+ percentage: (data.size / totalSize * 100).toFixed(2)
280
+ }))
281
+ };
282
+ }
283
+ /**
284
+ * Find largest chunks
285
+ */
286
+ getLargestChunks(limit = 10) {
287
+ return Array.from(this.chunks.entries()).sort((a, b) => b[1].size - a[1].size).slice(0, limit).map(([name, data]) => ({ name, ...data }));
288
+ }
289
+ /**
290
+ * Find slowest chunks
291
+ */
292
+ getSlowestChunks(limit = 10) {
293
+ return Array.from(this.chunks.entries()).sort((a, b) => b[1].loadTime - a[1].loadTime).slice(0, limit).map(([name, data]) => ({ name, ...data }));
294
+ }
295
+ };
296
+
297
+ // src/performance/cache.js
298
+ var LRUCache = class {
299
+ constructor(options = {}) {
300
+ this.maxSize = options.maxSize || 100;
301
+ this.ttl = options.ttl || null;
302
+ this.cache = /* @__PURE__ */ new Map();
303
+ this.accessOrder = [];
304
+ }
305
+ /**
306
+ * Get value from cache
307
+ */
308
+ get(key) {
309
+ if (!this.cache.has(key)) {
310
+ return void 0;
311
+ }
312
+ const entry = this.cache.get(key);
313
+ if (this.ttl && Date.now() - entry.timestamp > this.ttl) {
314
+ this.delete(key);
315
+ return void 0;
316
+ }
317
+ this.updateAccessOrder(key);
318
+ return entry.value;
319
+ }
320
+ /**
321
+ * Set value in cache
322
+ */
323
+ set(key, value) {
324
+ if (this.cache.has(key)) {
325
+ this.delete(key);
326
+ }
327
+ if (this.cache.size >= this.maxSize) {
328
+ this.evict();
329
+ }
330
+ this.cache.set(key, {
331
+ value,
332
+ timestamp: Date.now()
333
+ });
334
+ this.accessOrder.push(key);
335
+ return this;
336
+ }
337
+ /**
338
+ * Check if key exists
339
+ */
340
+ has(key) {
341
+ if (!this.cache.has(key)) {
342
+ return false;
343
+ }
344
+ const entry = this.cache.get(key);
345
+ if (this.ttl && Date.now() - entry.timestamp > this.ttl) {
346
+ this.delete(key);
347
+ return false;
348
+ }
349
+ return true;
350
+ }
351
+ /**
352
+ * Delete key
353
+ */
354
+ delete(key) {
355
+ this.cache.delete(key);
356
+ const index = this.accessOrder.indexOf(key);
357
+ if (index > -1) {
358
+ this.accessOrder.splice(index, 1);
359
+ }
360
+ return this;
361
+ }
362
+ /**
363
+ * Clear cache
364
+ */
365
+ clear() {
366
+ this.cache.clear();
367
+ this.accessOrder = [];
368
+ return this;
369
+ }
370
+ /**
371
+ * Get cache size
372
+ */
373
+ size() {
374
+ return this.cache.size;
375
+ }
376
+ /**
377
+ * Update access order
378
+ */
379
+ updateAccessOrder(key) {
380
+ const index = this.accessOrder.indexOf(key);
381
+ if (index > -1) {
382
+ this.accessOrder.splice(index, 1);
383
+ }
384
+ this.accessOrder.push(key);
385
+ }
386
+ /**
387
+ * Evict least recently used
388
+ */
389
+ evict() {
390
+ if (this.accessOrder.length > 0) {
391
+ const oldest = this.accessOrder.shift();
392
+ this.cache.delete(oldest);
393
+ }
394
+ }
395
+ /**
396
+ * Get all keys
397
+ */
398
+ keys() {
399
+ return Array.from(this.cache.keys());
400
+ }
401
+ /**
402
+ * Get all values
403
+ */
404
+ values() {
405
+ return Array.from(this.cache.values()).map((entry) => entry.value);
406
+ }
407
+ /**
408
+ * Get statistics
409
+ */
410
+ getStats() {
411
+ return {
412
+ size: this.cache.size,
413
+ maxSize: this.maxSize,
414
+ utilizationPercent: (this.cache.size / this.maxSize * 100).toFixed(2),
415
+ oldestKey: this.accessOrder[0],
416
+ newestKey: this.accessOrder[this.accessOrder.length - 1]
417
+ };
418
+ }
419
+ };
420
+ var MemoryCache = class {
421
+ constructor(options = {}) {
422
+ this.options = {
423
+ strategy: "lru",
424
+ // lru, lfu, fifo
425
+ maxSize: 100,
426
+ ttl: null,
427
+ ...options
428
+ };
429
+ this.cache = /* @__PURE__ */ new Map();
430
+ this.metadata = /* @__PURE__ */ new Map();
431
+ this.hits = 0;
432
+ this.misses = 0;
433
+ }
434
+ /**
435
+ * Get from cache
436
+ */
437
+ get(key) {
438
+ if (!this.cache.has(key)) {
439
+ this.misses++;
440
+ return void 0;
441
+ }
442
+ const entry = this.cache.get(key);
443
+ if (entry.ttl && Date.now() > entry.expiresAt) {
444
+ this.delete(key);
445
+ this.misses++;
446
+ return void 0;
447
+ }
448
+ this.updateMetadata(key);
449
+ this.hits++;
450
+ return entry.value;
451
+ }
452
+ /**
453
+ * Set in cache
454
+ */
455
+ set(key, value, options = {}) {
456
+ if (this.cache.size >= this.options.maxSize && !this.cache.has(key)) {
457
+ this.evict();
458
+ }
459
+ const ttl = options.ttl || this.options.ttl;
460
+ this.cache.set(key, {
461
+ value,
462
+ ttl,
463
+ expiresAt: ttl ? Date.now() + ttl : null,
464
+ createdAt: Date.now()
465
+ });
466
+ this.metadata.set(key, {
467
+ accessCount: 0,
468
+ lastAccess: Date.now()
469
+ });
470
+ return this;
471
+ }
472
+ /**
473
+ * Update metadata based on strategy
474
+ */
475
+ updateMetadata(key) {
476
+ const meta = this.metadata.get(key);
477
+ if (meta) {
478
+ meta.accessCount++;
479
+ meta.lastAccess = Date.now();
480
+ }
481
+ }
482
+ /**
483
+ * Evict based on strategy
484
+ */
485
+ evict() {
486
+ let keyToEvict;
487
+ switch (this.options.strategy) {
488
+ case "lru":
489
+ keyToEvict = this.findLRU();
490
+ break;
491
+ case "lfu":
492
+ keyToEvict = this.findLFU();
493
+ break;
494
+ case "fifo":
495
+ keyToEvict = this.findFIFO();
496
+ break;
497
+ default:
498
+ keyToEvict = this.cache.keys().next().value;
499
+ }
500
+ if (keyToEvict) {
501
+ this.delete(keyToEvict);
502
+ }
503
+ }
504
+ /**
505
+ * Find least recently used key
506
+ */
507
+ findLRU() {
508
+ let oldest = null;
509
+ let oldestTime = Infinity;
510
+ for (const [key, meta] of this.metadata.entries()) {
511
+ if (meta.lastAccess < oldestTime) {
512
+ oldestTime = meta.lastAccess;
513
+ oldest = key;
514
+ }
515
+ }
516
+ return oldest;
517
+ }
518
+ /**
519
+ * Find least frequently used key
520
+ */
521
+ findLFU() {
522
+ let leastUsed = null;
523
+ let minCount = Infinity;
524
+ for (const [key, meta] of this.metadata.entries()) {
525
+ if (meta.accessCount < minCount) {
526
+ minCount = meta.accessCount;
527
+ leastUsed = key;
528
+ }
529
+ }
530
+ return leastUsed;
531
+ }
532
+ /**
533
+ * Find first in (oldest)
534
+ */
535
+ findFIFO() {
536
+ let oldest = null;
537
+ let oldestTime = Infinity;
538
+ for (const [key, entry] of this.cache.entries()) {
539
+ if (entry.createdAt < oldestTime) {
540
+ oldestTime = entry.createdAt;
541
+ oldest = key;
542
+ }
543
+ }
544
+ return oldest;
545
+ }
546
+ /**
547
+ * Check if key exists
548
+ */
549
+ has(key) {
550
+ return this.cache.has(key);
551
+ }
552
+ /**
553
+ * Delete key
554
+ */
555
+ delete(key) {
556
+ this.cache.delete(key);
557
+ this.metadata.delete(key);
558
+ return this;
559
+ }
560
+ /**
561
+ * Clear cache
562
+ */
563
+ clear() {
564
+ this.cache.clear();
565
+ this.metadata.clear();
566
+ this.hits = 0;
567
+ this.misses = 0;
568
+ return this;
569
+ }
570
+ /**
571
+ * Get cache statistics
572
+ */
573
+ getStats() {
574
+ const total = this.hits + this.misses;
575
+ const hitRate = total > 0 ? (this.hits / total * 100).toFixed(2) : 0;
576
+ return {
577
+ size: this.cache.size,
578
+ maxSize: this.options.maxSize,
579
+ hits: this.hits,
580
+ misses: this.misses,
581
+ hitRate: `${hitRate}%`,
582
+ strategy: this.options.strategy
583
+ };
584
+ }
585
+ };
586
+ var MemoCache = class {
587
+ constructor(options = {}) {
588
+ this.cache = new LRUCache(options);
589
+ this.keyGenerator = options.keyGenerator || this.defaultKeyGenerator;
590
+ }
591
+ /**
592
+ * Default key generator
593
+ */
594
+ defaultKeyGenerator(...args) {
595
+ return JSON.stringify(args);
596
+ }
597
+ /**
598
+ * Memoize a function
599
+ */
600
+ memoize(fn) {
601
+ return (...args) => {
602
+ const key = this.keyGenerator(...args);
603
+ if (this.cache.has(key)) {
604
+ return this.cache.get(key);
605
+ }
606
+ const result = fn(...args);
607
+ this.cache.set(key, result);
608
+ return result;
609
+ };
610
+ }
611
+ /**
612
+ * Clear memoization cache
613
+ */
614
+ clear() {
615
+ this.cache.clear();
616
+ }
617
+ /**
618
+ * Get statistics
619
+ */
620
+ getStats() {
621
+ return this.cache.getStats();
622
+ }
623
+ };
624
+ var RenderCache = class {
625
+ constructor(options = {}) {
626
+ this.cache = new MemoryCache({
627
+ maxSize: options.maxSize || 50,
628
+ ttl: options.ttl || 6e4,
629
+ // 1 minute default
630
+ strategy: "lru"
631
+ });
632
+ }
633
+ /**
634
+ * Generate cache key for component
635
+ */
636
+ generateKey(component, props) {
637
+ const componentName = component.name || "anonymous";
638
+ const propsKey = this.hashProps(props);
639
+ return `${componentName}:${propsKey}`;
640
+ }
641
+ /**
642
+ * Hash props for cache key
643
+ */
644
+ hashProps(props) {
645
+ try {
646
+ return JSON.stringify(props, Object.keys(props).sort());
647
+ } catch {
648
+ return String(Date.now());
649
+ }
650
+ }
651
+ /**
652
+ * Get cached render
653
+ */
654
+ get(component, props) {
655
+ const key = this.generateKey(component, props);
656
+ return this.cache.get(key);
657
+ }
658
+ /**
659
+ * Cache render result
660
+ */
661
+ set(component, props, result, options = {}) {
662
+ const key = this.generateKey(component, props);
663
+ this.cache.set(key, result, options);
664
+ }
665
+ /**
666
+ * Clear cache
667
+ */
668
+ clear() {
669
+ this.cache.clear();
670
+ }
671
+ /**
672
+ * Get statistics
673
+ */
674
+ getStats() {
675
+ return this.cache.getStats();
676
+ }
677
+ };
678
+ function createCache(type = "lru", options = {}) {
679
+ switch (type) {
680
+ case "lru":
681
+ return new LRUCache(options);
682
+ case "memory":
683
+ return new MemoryCache(options);
684
+ case "memo":
685
+ return new MemoCache(options);
686
+ case "render":
687
+ return new RenderCache(options);
688
+ default:
689
+ return new LRUCache(options);
690
+ }
691
+ }
692
+ function memoize(fn, options = {}) {
693
+ const cache = new MemoCache(options);
694
+ return cache.memoize(fn);
695
+ }
696
+
697
+ // src/performance/lazy-loading.js
698
+ var LazyLoader = class {
699
+ constructor(options = {}) {
700
+ this.options = {
701
+ rootMargin: "50px",
702
+ threshold: 0.01,
703
+ ...options
704
+ };
705
+ this.observer = null;
706
+ this.observed = /* @__PURE__ */ new Set();
707
+ this.loaded = /* @__PURE__ */ new Set();
708
+ this.initObserver();
709
+ }
710
+ /**
711
+ * Initialize Intersection Observer
712
+ */
713
+ initObserver() {
714
+ if (typeof IntersectionObserver === "undefined") {
715
+ return;
716
+ }
717
+ this.observer = new IntersectionObserver(
718
+ (entries) => this.handleIntersection(entries),
719
+ {
720
+ rootMargin: this.options.rootMargin,
721
+ threshold: this.options.threshold
722
+ }
723
+ );
724
+ }
725
+ /**
726
+ * Handle intersection
727
+ */
728
+ handleIntersection(entries) {
729
+ entries.forEach((entry) => {
730
+ if (entry.isIntersecting) {
731
+ this.loadElement(entry.target);
732
+ }
733
+ });
734
+ }
735
+ /**
736
+ * Observe an element
737
+ */
738
+ observe(element) {
739
+ if (!this.observer || this.observed.has(element)) {
740
+ return;
741
+ }
742
+ this.observer.observe(element);
743
+ this.observed.add(element);
744
+ }
745
+ /**
746
+ * Unobserve an element
747
+ */
748
+ unobserve(element) {
749
+ if (!this.observer) {
750
+ return;
751
+ }
752
+ this.observer.unobserve(element);
753
+ this.observed.delete(element);
754
+ }
755
+ /**
756
+ * Load an element
757
+ */
758
+ loadElement(element) {
759
+ if (this.loaded.has(element)) {
760
+ return;
761
+ }
762
+ if (element.tagName === "IMG") {
763
+ this.loadImage(element);
764
+ } else if (element.tagName === "SCRIPT") {
765
+ this.loadScript(element);
766
+ } else if (element.tagName === "IFRAME") {
767
+ this.loadIframe(element);
768
+ }
769
+ this.loaded.add(element);
770
+ this.unobserve(element);
771
+ }
772
+ /**
773
+ * Load image
774
+ */
775
+ loadImage(img) {
776
+ const src = img.dataset.src;
777
+ const srcset = img.dataset.srcset;
778
+ if (src) {
779
+ img.src = src;
780
+ }
781
+ if (srcset) {
782
+ img.srcset = srcset;
783
+ }
784
+ img.classList.add("loaded");
785
+ }
786
+ /**
787
+ * Load script
788
+ */
789
+ loadScript(script) {
790
+ const src = script.dataset.src;
791
+ if (src) {
792
+ script.src = src;
793
+ }
794
+ }
795
+ /**
796
+ * Load iframe
797
+ */
798
+ loadIframe(iframe) {
799
+ const src = iframe.dataset.src;
800
+ if (src) {
801
+ iframe.src = src;
802
+ }
803
+ }
804
+ /**
805
+ * Disconnect observer
806
+ */
807
+ disconnect() {
808
+ if (this.observer) {
809
+ this.observer.disconnect();
810
+ }
811
+ this.observed.clear();
812
+ }
813
+ };
814
+ var ImageLazyLoader = class {
815
+ constructor(options = {}) {
816
+ this.options = {
817
+ placeholder: 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1 1"%3E%3C/svg%3E',
818
+ loadingClass: "lazy-loading",
819
+ loadedClass: "lazy-loaded",
820
+ errorClass: "lazy-error",
821
+ ...options
822
+ };
823
+ this.loader = new LazyLoader(options);
824
+ }
825
+ /**
826
+ * Create lazy image component
827
+ */
828
+ createImage(src, options = {}) {
829
+ return {
830
+ img: {
831
+ src: this.options.placeholder,
832
+ "data-src": src,
833
+ "data-srcset": options.srcset,
834
+ alt: options.alt || "",
835
+ className: this.options.loadingClass,
836
+ loading: "lazy",
837
+ onload: `this.classList.add("${this.options.loadedClass}")`,
838
+ onerror: `this.classList.add("${this.options.errorClass}")`
839
+ }
840
+ };
841
+ }
842
+ /**
843
+ * Observe images
844
+ */
845
+ observe(selector = "img[data-src]") {
846
+ if (typeof document === "undefined") {
847
+ return;
848
+ }
849
+ const images = document.querySelectorAll(selector);
850
+ images.forEach((img) => this.loader.observe(img));
851
+ }
852
+ /**
853
+ * Load all images immediately
854
+ */
855
+ loadAll() {
856
+ this.observed.forEach((element) => {
857
+ this.loader.loadElement(element);
858
+ });
859
+ }
860
+ };
861
+ var ResourcePreloader = class {
862
+ constructor() {
863
+ this.preloaded = /* @__PURE__ */ new Set();
864
+ this.preloading = /* @__PURE__ */ new Map();
865
+ }
866
+ /**
867
+ * Preload an image
868
+ */
869
+ async preloadImage(src) {
870
+ if (this.preloaded.has(src)) {
871
+ return;
872
+ }
873
+ if (this.preloading.has(src)) {
874
+ return this.preloading.get(src);
875
+ }
876
+ const promise = new Promise((resolve, reject) => {
877
+ const img = new Image();
878
+ img.onload = () => {
879
+ this.preloaded.add(src);
880
+ this.preloading.delete(src);
881
+ resolve(img);
882
+ };
883
+ img.onerror = () => {
884
+ this.preloading.delete(src);
885
+ reject(new Error(`Failed to preload image: ${src}`));
886
+ };
887
+ img.src = src;
888
+ });
889
+ this.preloading.set(src, promise);
890
+ return promise;
891
+ }
892
+ /**
893
+ * Preload multiple images
894
+ */
895
+ async preloadImages(sources) {
896
+ return Promise.all(sources.map((src) => this.preloadImage(src)));
897
+ }
898
+ /**
899
+ * Preload a script
900
+ */
901
+ async preloadScript(src) {
902
+ if (this.preloaded.has(src)) {
903
+ return;
904
+ }
905
+ if (this.preloading.has(src)) {
906
+ return this.preloading.get(src);
907
+ }
908
+ const promise = new Promise((resolve, reject) => {
909
+ const link = document.createElement("link");
910
+ link.rel = "preload";
911
+ link.as = "script";
912
+ link.href = src;
913
+ link.onload = () => {
914
+ this.preloaded.add(src);
915
+ this.preloading.delete(src);
916
+ resolve();
917
+ };
918
+ link.onerror = () => {
919
+ this.preloading.delete(src);
920
+ reject(new Error(`Failed to preload script: ${src}`));
921
+ };
922
+ document.head.appendChild(link);
923
+ });
924
+ this.preloading.set(src, promise);
925
+ return promise;
926
+ }
927
+ /**
928
+ * Prefetch a resource
929
+ */
930
+ prefetch(href, options = {}) {
931
+ if (typeof document === "undefined") {
932
+ return;
933
+ }
934
+ const link = document.createElement("link");
935
+ link.rel = "prefetch";
936
+ link.href = href;
937
+ if (options.as) {
938
+ link.as = options.as;
939
+ }
940
+ document.head.appendChild(link);
941
+ }
942
+ /**
943
+ * Check if resource is preloaded
944
+ */
945
+ isPreloaded(src) {
946
+ return this.preloaded.has(src);
947
+ }
948
+ /**
949
+ * Clear preload cache
950
+ */
951
+ clear() {
952
+ this.preloaded.clear();
953
+ this.preloading.clear();
954
+ }
955
+ };
956
+ var ProgressiveImageLoader = class {
957
+ /**
958
+ * Create progressive image component
959
+ */
960
+ createImage(lowResSrc, highResSrc, options = {}) {
961
+ return {
962
+ div: {
963
+ className: "progressive-image",
964
+ style: options.style || {},
965
+ children: [
966
+ {
967
+ img: {
968
+ src: lowResSrc,
969
+ className: "progressive-image-low",
970
+ alt: options.alt || "",
971
+ style: "filter: blur(10px); transition: opacity 0.3s;"
972
+ }
973
+ },
974
+ {
975
+ img: {
976
+ "data-src": highResSrc,
977
+ className: "progressive-image-high",
978
+ alt: options.alt || "",
979
+ style: "opacity: 0; transition: opacity 0.3s;",
980
+ onload: "this.style.opacity = 1; this.previousElementSibling.style.opacity = 0;"
981
+ }
982
+ }
983
+ ]
984
+ }
985
+ };
986
+ }
987
+ };
988
+ function createLazyLoader(options = {}) {
989
+ return new LazyLoader(options);
990
+ }
991
+ function createImageLazyLoader(options = {}) {
992
+ return new ImageLazyLoader(options);
993
+ }
994
+ function createPreloader() {
995
+ return new ResourcePreloader();
996
+ }
997
+ function lazyImage(src, options = {}) {
998
+ const loader = new ImageLazyLoader();
999
+ return loader.createImage(src, options);
1000
+ }
1001
+ function progressiveImage(lowRes, highRes, options = {}) {
1002
+ const loader = new ProgressiveImageLoader();
1003
+ return loader.createImage(lowRes, highRes, options);
1004
+ }
1005
+
1006
+ // src/performance-dashboard.js
1007
+ var PerformanceDashboard = class {
1008
+ constructor(options = {}) {
1009
+ this.options = {
1010
+ updateInterval: options.updateInterval || 5e3,
1011
+ maxHistoryPoints: options.maxHistoryPoints || 100,
1012
+ enableAlerts: options.enableAlerts !== false,
1013
+ enableRecommendations: options.enableRecommendations !== false,
1014
+ colorOutput: options.colorOutput !== false,
1015
+ ...options
1016
+ };
1017
+ this.metrics = {
1018
+ api: {
1019
+ requests: 0,
1020
+ averageTime: 0,
1021
+ cacheHits: 0,
1022
+ cacheMisses: 0,
1023
+ staticRoutes: 0,
1024
+ dynamicRoutes: 0,
1025
+ history: []
1026
+ },
1027
+ components: {
1028
+ renders: 0,
1029
+ averageTime: 0,
1030
+ cacheHits: 0,
1031
+ cacheMisses: 0,
1032
+ staticComponents: 0,
1033
+ dynamicComponents: 0,
1034
+ memoryUsage: 0,
1035
+ history: []
1036
+ },
1037
+ fullstack: {
1038
+ totalRequests: 0,
1039
+ averageTime: 0,
1040
+ errors: 0,
1041
+ bottlenecks: [],
1042
+ history: []
1043
+ }
1044
+ };
1045
+ this.alerts = [];
1046
+ this.recommendations = [];
1047
+ this.startTime = Date.now();
1048
+ this.updateTimer = null;
1049
+ }
1050
+ /**
1051
+ * Start monitoring performance
1052
+ */
1053
+ startMonitoring() {
1054
+ if (this.updateTimer) return;
1055
+ this.updateTimer = setInterval(() => {
1056
+ this.updateMetrics();
1057
+ this.generateAlerts();
1058
+ this.generateRecommendations();
1059
+ }, this.options.updateInterval);
1060
+ }
1061
+ /**
1062
+ * Stop monitoring
1063
+ */
1064
+ stopMonitoring() {
1065
+ if (this.updateTimer) {
1066
+ clearInterval(this.updateTimer);
1067
+ this.updateTimer = null;
1068
+ }
1069
+ }
1070
+ /**
1071
+ * Record API request metrics
1072
+ */
1073
+ recordAPIRequest(duration, routeType, cacheHit = false) {
1074
+ this.metrics.api.requests++;
1075
+ this.metrics.api.averageTime = this.updateAverage(
1076
+ this.metrics.api.averageTime,
1077
+ duration,
1078
+ this.metrics.api.requests
1079
+ );
1080
+ if (cacheHit) {
1081
+ this.metrics.api.cacheHits++;
1082
+ } else {
1083
+ this.metrics.api.cacheMisses++;
1084
+ }
1085
+ if (routeType === "static") {
1086
+ this.metrics.api.staticRoutes++;
1087
+ } else {
1088
+ this.metrics.api.dynamicRoutes++;
1089
+ }
1090
+ this.addToHistory("api", {
1091
+ timestamp: Date.now(),
1092
+ duration,
1093
+ routeType,
1094
+ cacheHit
1095
+ });
1096
+ }
1097
+ /**
1098
+ * Record component render metrics
1099
+ */
1100
+ recordComponentRender(duration, componentType, cacheHit = false, memoryDelta = 0) {
1101
+ this.metrics.components.renders++;
1102
+ this.metrics.components.averageTime = this.updateAverage(
1103
+ this.metrics.components.averageTime,
1104
+ duration,
1105
+ this.metrics.components.renders
1106
+ );
1107
+ if (cacheHit) {
1108
+ this.metrics.components.cacheHits++;
1109
+ } else {
1110
+ this.metrics.components.cacheMisses++;
1111
+ }
1112
+ if (componentType === "static") {
1113
+ this.metrics.components.staticComponents++;
1114
+ } else {
1115
+ this.metrics.components.dynamicComponents++;
1116
+ }
1117
+ this.metrics.components.memoryUsage += memoryDelta;
1118
+ this.addToHistory("components", {
1119
+ timestamp: Date.now(),
1120
+ duration,
1121
+ componentType,
1122
+ cacheHit,
1123
+ memoryDelta
1124
+ });
1125
+ }
1126
+ /**
1127
+ * Record full-stack request metrics
1128
+ */
1129
+ recordFullStackRequest(duration, error = null, bottlenecks = []) {
1130
+ this.metrics.fullstack.totalRequests++;
1131
+ this.metrics.fullstack.averageTime = this.updateAverage(
1132
+ this.metrics.fullstack.averageTime,
1133
+ duration,
1134
+ this.metrics.fullstack.totalRequests
1135
+ );
1136
+ if (error) {
1137
+ this.metrics.fullstack.errors++;
1138
+ }
1139
+ this.metrics.fullstack.bottlenecks = bottlenecks;
1140
+ this.addToHistory("fullstack", {
1141
+ timestamp: Date.now(),
1142
+ duration,
1143
+ error,
1144
+ bottlenecks
1145
+ });
1146
+ }
1147
+ /**
1148
+ * Update metrics from external sources
1149
+ */
1150
+ updateMetrics() {
1151
+ const now = Date.now();
1152
+ const uptime = now - this.startTime;
1153
+ const apiRate = this.metrics.api.requests / (uptime / 1e3);
1154
+ const componentRate = this.metrics.components.renders / (uptime / 1e3);
1155
+ const fullStackRate = this.metrics.fullstack.totalRequests / (uptime / 1e3);
1156
+ return {
1157
+ apiRate,
1158
+ componentRate,
1159
+ fullStackRate,
1160
+ uptime
1161
+ };
1162
+ }
1163
+ /**
1164
+ * Generate performance alerts
1165
+ */
1166
+ generateAlerts() {
1167
+ this.alerts = [];
1168
+ if (this.metrics.api.averageTime > 50) {
1169
+ this.alerts.push({
1170
+ type: "warning",
1171
+ category: "api",
1172
+ message: `API response time is high: ${this.metrics.api.averageTime.toFixed(2)}ms`,
1173
+ threshold: 50,
1174
+ current: this.metrics.api.averageTime
1175
+ });
1176
+ }
1177
+ const apiCacheHitRate = this.getCacheHitRate("api");
1178
+ if (apiCacheHitRate < 80) {
1179
+ this.alerts.push({
1180
+ type: "warning",
1181
+ category: "api",
1182
+ message: `API cache hit rate is low: ${apiCacheHitRate.toFixed(1)}%`,
1183
+ threshold: 80,
1184
+ current: apiCacheHitRate
1185
+ });
1186
+ }
1187
+ if (this.metrics.components.averageTime > 20) {
1188
+ this.alerts.push({
1189
+ type: "warning",
1190
+ category: "components",
1191
+ message: `Component render time is high: ${this.metrics.components.averageTime.toFixed(2)}ms`,
1192
+ threshold: 20,
1193
+ current: this.metrics.components.averageTime
1194
+ });
1195
+ }
1196
+ const componentCacheHitRate = this.getCacheHitRate("components");
1197
+ if (componentCacheHitRate < 90) {
1198
+ this.alerts.push({
1199
+ type: "warning",
1200
+ category: "components",
1201
+ message: `Component cache hit rate is low: ${componentCacheHitRate.toFixed(1)}%`,
1202
+ threshold: 90,
1203
+ current: componentCacheHitRate
1204
+ });
1205
+ }
1206
+ if (this.metrics.fullstack.errors > 0) {
1207
+ this.alerts.push({
1208
+ type: "error",
1209
+ category: "fullstack",
1210
+ message: `${this.metrics.fullstack.errors} errors detected`,
1211
+ threshold: 0,
1212
+ current: this.metrics.fullstack.errors
1213
+ });
1214
+ }
1215
+ }
1216
+ /**
1217
+ * Generate performance recommendations
1218
+ */
1219
+ generateRecommendations() {
1220
+ this.recommendations = [];
1221
+ const staticRouteRatio = this.metrics.api.staticRoutes / Math.max(this.metrics.api.requests, 1);
1222
+ if (staticRouteRatio < 0.7) {
1223
+ this.recommendations.push({
1224
+ type: "optimization",
1225
+ category: "api",
1226
+ message: "Consider adding more static routes to improve smart routing efficiency",
1227
+ impact: "high",
1228
+ effort: "low"
1229
+ });
1230
+ }
1231
+ const apiCacheHitRate = this.getCacheHitRate("api");
1232
+ if (apiCacheHitRate < 90) {
1233
+ this.recommendations.push({
1234
+ type: "optimization",
1235
+ category: "api",
1236
+ message: "Increase API cache size or TTL to improve cache hit rate",
1237
+ impact: "medium",
1238
+ effort: "low"
1239
+ });
1240
+ }
1241
+ const staticComponentRatio = this.metrics.components.staticComponents / Math.max(this.metrics.components.renders, 1);
1242
+ if (staticComponentRatio < 0.8) {
1243
+ this.recommendations.push({
1244
+ type: "optimization",
1245
+ category: "components",
1246
+ message: "More components could be optimized as static for better caching",
1247
+ impact: "high",
1248
+ effort: "medium"
1249
+ });
1250
+ }
1251
+ if (this.metrics.components.memoryUsage > 100 * 1024 * 1024) {
1252
+ this.recommendations.push({
1253
+ type: "optimization",
1254
+ category: "memory",
1255
+ message: "Memory usage is high. Consider reducing cache size or implementing memory cleanup",
1256
+ impact: "medium",
1257
+ effort: "medium"
1258
+ });
1259
+ }
1260
+ }
1261
+ /**
1262
+ * Get cache hit rate for category
1263
+ */
1264
+ getCacheHitRate(category) {
1265
+ const metrics = this.metrics[category];
1266
+ if (!metrics || !metrics.cacheHits) return 0;
1267
+ const total = metrics.cacheHits + metrics.cacheMisses;
1268
+ return total > 0 ? metrics.cacheHits / total * 100 : 0;
1269
+ }
1270
+ /**
1271
+ * Update running average
1272
+ */
1273
+ updateAverage(current, newValue, count) {
1274
+ return (current * (count - 1) + newValue) / count;
1275
+ }
1276
+ /**
1277
+ * Add data point to history
1278
+ */
1279
+ addToHistory(category, data) {
1280
+ if (!this.metrics[category].history) {
1281
+ this.metrics[category].history = [];
1282
+ }
1283
+ this.metrics[category].history.push(data);
1284
+ if (this.metrics[category].history.length > this.options.maxHistoryPoints) {
1285
+ this.metrics[category].history = this.metrics[category].history.slice(-this.options.maxHistoryPoints);
1286
+ }
1287
+ }
1288
+ /**
1289
+ * Generate dashboard visualization
1290
+ */
1291
+ generateDashboard() {
1292
+ const lines = [];
1293
+ if (this.options.colorOutput) {
1294
+ lines.push(this.colorize("\u{1F4CA} Coherent.js Performance Dashboard", "cyan"));
1295
+ lines.push(this.colorize("\u2550".repeat(50), "cyan"));
1296
+ } else {
1297
+ lines.push("\u{1F4CA} Coherent.js Performance Dashboard");
1298
+ lines.push("\u2550".repeat(50));
1299
+ }
1300
+ const uptime = (Date.now() - this.startTime) / 1e3;
1301
+ lines.push(`\u23F1\uFE0F Uptime: ${uptime.toFixed(1)}s`);
1302
+ lines.push("");
1303
+ lines.push("\u{1F680} API Performance");
1304
+ lines.push("\u2500".repeat(20));
1305
+ const apiCacheHitRate = this.getCacheHitRate("api");
1306
+ lines.push(` Requests: ${this.metrics.api.requests} (${(this.metrics.api.requests / uptime).toFixed(1)} req/s)`);
1307
+ lines.push(` Avg Time: ${this.metrics.api.averageTime.toFixed(2)}ms`);
1308
+ lines.push(` Cache Hit Rate: ${apiCacheHitRate.toFixed(1)}%`);
1309
+ lines.push(` Static Routes: ${this.metrics.api.staticRoutes}/${this.metrics.api.requests} (${(this.metrics.api.staticRoutes / Math.max(this.metrics.api.requests, 1) * 100).toFixed(1)}%)`);
1310
+ lines.push("");
1311
+ lines.push("\u{1F3D7}\uFE0F Component Performance");
1312
+ lines.push("\u2500".repeat(25));
1313
+ const componentCacheHitRate = this.getCacheHitRate("components");
1314
+ lines.push(` Renders: ${this.metrics.components.renders} (${(this.metrics.components.renders / uptime).toFixed(1)} renders/s)`);
1315
+ lines.push(` Avg Time: ${this.metrics.components.averageTime.toFixed(2)}ms`);
1316
+ lines.push(` Cache Hit Rate: ${componentCacheHitRate.toFixed(1)}%`);
1317
+ lines.push(` Static Components: ${this.metrics.components.staticComponents}/${this.metrics.components.renders} (${(this.metrics.components.staticComponents / Math.max(this.metrics.components.renders, 1) * 100).toFixed(1)}%)`);
1318
+ lines.push(` Memory Usage: ${(this.metrics.components.memoryUsage / 1024 / 1024).toFixed(1)}MB`);
1319
+ lines.push("");
1320
+ lines.push("\u{1F310} Full-Stack Performance");
1321
+ lines.push("\u2500".repeat(26));
1322
+ lines.push(` Total Requests: ${this.metrics.fullstack.totalRequests} (${(this.metrics.fullstack.totalRequests / uptime).toFixed(1)} req/s)`);
1323
+ lines.push(` Avg Time: ${this.metrics.fullstack.averageTime.toFixed(2)}ms`);
1324
+ lines.push(` Errors: ${this.metrics.fullstack.errors}`);
1325
+ lines.push("");
1326
+ if (this.alerts.length > 0) {
1327
+ lines.push("\u26A0\uFE0F Performance Alerts");
1328
+ lines.push("\u2500".repeat(22));
1329
+ this.alerts.forEach((alert) => {
1330
+ const icon = alert.type === "error" ? "\u274C" : "\u26A0\uFE0F";
1331
+ lines.push(` ${icon} ${alert.message}`);
1332
+ });
1333
+ lines.push("");
1334
+ }
1335
+ if (this.recommendations.length > 0) {
1336
+ lines.push("\u{1F4A1} Optimization Recommendations");
1337
+ lines.push("\u2500".repeat(30));
1338
+ this.recommendations.forEach((rec) => {
1339
+ const impact = rec.impact === "high" ? "\u{1F525}" : rec.impact === "medium" ? "\u26A1" : "\u{1F4A4}";
1340
+ lines.push(` ${impact} ${rec.message} (${rec.effort} effort)`);
1341
+ });
1342
+ lines.push("");
1343
+ }
1344
+ const score = this.calculatePerformanceScore();
1345
+ const scoreColor = score >= 90 ? "green" : score >= 70 ? "yellow" : "red";
1346
+ lines.push(`Performance Score: ${this.colorize(`${score.toFixed(1)}/100`, scoreColor)}`);
1347
+ return lines.join("\n");
1348
+ }
1349
+ /**
1350
+ * Calculate overall performance score
1351
+ */
1352
+ calculatePerformanceScore() {
1353
+ let score = 100;
1354
+ if (this.metrics.api.averageTime > 50) score -= 10;
1355
+ if (this.metrics.api.averageTime > 100) score -= 10;
1356
+ if (this.getCacheHitRate("api") < 90) score -= 10;
1357
+ if (this.metrics.components.averageTime > 20) score -= 10;
1358
+ if (this.metrics.components.averageTime > 50) score -= 10;
1359
+ if (this.getCacheHitRate("components") < 95) score -= 10;
1360
+ if (this.metrics.fullstack.errors > 0) score -= Math.min(20, this.metrics.fullstack.errors * 5);
1361
+ return Math.max(0, score);
1362
+ }
1363
+ /**
1364
+ * Add color to text
1365
+ */
1366
+ colorize(text, color) {
1367
+ if (!this.options.colorOutput) return text;
1368
+ const colors = {
1369
+ black: "\x1B[30m",
1370
+ red: "\x1B[31m",
1371
+ green: "\x1B[32m",
1372
+ yellow: "\x1B[33m",
1373
+ blue: "\x1B[34m",
1374
+ magenta: "\x1B[35m",
1375
+ cyan: "\x1B[36m",
1376
+ white: "\x1B[37m",
1377
+ gray: "\x1B[90m"
1378
+ };
1379
+ const reset = "\x1B[0m";
1380
+ return `${colors[color] || ""}${text}${reset}`;
1381
+ }
1382
+ /**
1383
+ * Export metrics as JSON
1384
+ */
1385
+ exportMetrics() {
1386
+ return {
1387
+ timestamp: Date.now(),
1388
+ uptime: Date.now() - this.startTime,
1389
+ metrics: { ...this.metrics },
1390
+ alerts: [...this.alerts],
1391
+ recommendations: [...this.recommendations],
1392
+ performanceScore: this.calculatePerformanceScore()
1393
+ };
1394
+ }
1395
+ /**
1396
+ * Reset all metrics
1397
+ */
1398
+ reset() {
1399
+ this.metrics = {
1400
+ api: { requests: 0, averageTime: 0, cacheHits: 0, cacheMisses: 0, staticRoutes: 0, dynamicRoutes: 0, history: [] },
1401
+ components: { renders: 0, averageTime: 0, cacheHits: 0, cacheMisses: 0, staticComponents: 0, dynamicComponents: 0, memoryUsage: 0, history: [] },
1402
+ fullstack: { totalRequests: 0, averageTime: 0, errors: 0, bottlenecks: [], history: [] }
1403
+ };
1404
+ this.alerts = [];
1405
+ this.recommendations = [];
1406
+ this.startTime = Date.now();
1407
+ }
1408
+ };
1409
+ function createPerformanceDashboard(options = {}) {
1410
+ return new PerformanceDashboard(options);
1411
+ }
1412
+ function showPerformanceDashboard(dashboard) {
1413
+ const output = dashboard.generateDashboard();
1414
+ console.log(output);
1415
+ return dashboard;
1416
+ }
1417
+ export {
1418
+ BundleAnalyzer,
1419
+ CodeSplitter,
1420
+ ImageLazyLoader,
1421
+ LRUCache,
1422
+ LazyLoader,
1423
+ MemoCache,
1424
+ MemoryCache,
1425
+ PerformanceDashboard,
1426
+ ProgressiveImageLoader,
1427
+ RenderCache,
1428
+ ResourcePreloader,
1429
+ createCache,
1430
+ createCodeSplitter,
1431
+ createImageLazyLoader,
1432
+ createLazyLoader,
1433
+ createPerformanceDashboard,
1434
+ createPreloader,
1435
+ createRouteSplitter,
1436
+ lazy,
1437
+ lazyImage,
1438
+ memoize,
1439
+ progressiveImage,
1440
+ showPerformanceDashboard,
1441
+ splitComponent
1442
+ };
1443
+ //# sourceMappingURL=index.js.map