@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,259 @@
1
+ /**
2
+ * Debounce and throttle utilities for ProteusJS
3
+ * Optimizes performance by controlling function execution frequency
4
+ */
5
+
6
+ export interface DebounceOptions {
7
+ leading?: boolean;
8
+ trailing?: boolean;
9
+ maxWait?: number;
10
+ }
11
+
12
+ export interface ThrottleOptions {
13
+ leading?: boolean;
14
+ trailing?: boolean;
15
+ }
16
+
17
+ /**
18
+ * Debounce function execution
19
+ */
20
+ export function debounce<T extends (...args: unknown[]) => unknown>(
21
+ func: T,
22
+ wait: number,
23
+ options: DebounceOptions = {}
24
+ ): T & { cancel: () => void; flush: () => ReturnType<T> | undefined } {
25
+ const { leading = false, trailing = true, maxWait } = options;
26
+
27
+ let timeoutId: number | null = null;
28
+ let maxTimeoutId: number | null = null;
29
+ let lastCallTime: number | undefined;
30
+ let lastInvokeTime = 0;
31
+ let lastArgs: Parameters<T> | undefined;
32
+ let lastThis: unknown;
33
+ let result: ReturnType<T> | undefined;
34
+
35
+ function invokeFunc(time: number): ReturnType<T> {
36
+ const args = lastArgs!;
37
+ const thisArg = lastThis;
38
+
39
+ lastArgs = undefined;
40
+ lastThis = undefined;
41
+ lastInvokeTime = time;
42
+ result = func.apply(thisArg, args) as ReturnType<T>;
43
+ return result!;
44
+ }
45
+
46
+ function leadingEdge(time: number): ReturnType<T> {
47
+ lastInvokeTime = time;
48
+ timeoutId = window.setTimeout(timerExpired, wait);
49
+ return leading ? invokeFunc(time) : result!;
50
+ }
51
+
52
+ function remainingWait(time: number): number {
53
+ const timeSinceLastCall = time - lastCallTime!;
54
+ const timeSinceLastInvoke = time - lastInvokeTime;
55
+ const timeWaiting = wait - timeSinceLastCall;
56
+
57
+ return maxWait !== undefined
58
+ ? Math.min(timeWaiting, maxWait - timeSinceLastInvoke)
59
+ : timeWaiting;
60
+ }
61
+
62
+ function shouldInvoke(time: number): boolean {
63
+ const timeSinceLastCall = time - lastCallTime!;
64
+ const timeSinceLastInvoke = time - lastInvokeTime;
65
+
66
+ return (
67
+ lastCallTime === undefined ||
68
+ timeSinceLastCall >= wait ||
69
+ timeSinceLastCall < 0 ||
70
+ (maxWait !== undefined && timeSinceLastInvoke >= maxWait)
71
+ );
72
+ }
73
+
74
+ function timerExpired(): ReturnType<T> | undefined {
75
+ const time = Date.now();
76
+ if (shouldInvoke(time)) {
77
+ return trailingEdge(time);
78
+ }
79
+ timeoutId = window.setTimeout(timerExpired, remainingWait(time));
80
+ return undefined;
81
+ }
82
+
83
+ function trailingEdge(time: number): ReturnType<T> | undefined {
84
+ timeoutId = null;
85
+
86
+ if (trailing && lastArgs) {
87
+ return invokeFunc(time);
88
+ }
89
+ lastArgs = undefined;
90
+ lastThis = undefined;
91
+ return result;
92
+ }
93
+
94
+ function cancel(): void {
95
+ if (timeoutId !== null) {
96
+ clearTimeout(timeoutId);
97
+ timeoutId = null;
98
+ }
99
+ if (maxTimeoutId !== null) {
100
+ clearTimeout(maxTimeoutId);
101
+ maxTimeoutId = null;
102
+ }
103
+ lastInvokeTime = 0;
104
+ lastArgs = undefined;
105
+ lastCallTime = undefined;
106
+ lastThis = undefined;
107
+ }
108
+
109
+ function flush(): ReturnType<T> | undefined {
110
+ return timeoutId === null ? result : trailingEdge(Date.now());
111
+ }
112
+
113
+ function debounced(this: unknown, ...args: Parameters<T>): ReturnType<T> | undefined {
114
+ const time = Date.now();
115
+ const isInvoking = shouldInvoke(time);
116
+
117
+ lastArgs = args;
118
+ lastThis = this;
119
+ lastCallTime = time;
120
+
121
+ if (isInvoking) {
122
+ if (timeoutId === null) {
123
+ return leadingEdge(lastCallTime);
124
+ }
125
+ if (maxWait !== undefined) {
126
+ timeoutId = window.setTimeout(timerExpired, wait);
127
+ return invokeFunc(lastCallTime);
128
+ }
129
+ }
130
+ if (timeoutId === null) {
131
+ timeoutId = window.setTimeout(timerExpired, wait);
132
+ }
133
+ return result;
134
+ }
135
+
136
+ debounced.cancel = cancel;
137
+ debounced.flush = flush;
138
+
139
+ return debounced as T & { cancel: () => void; flush: () => ReturnType<T> | undefined };
140
+ }
141
+
142
+ /**
143
+ * Throttle function execution
144
+ */
145
+ export function throttle<T extends (...args: unknown[]) => unknown>(
146
+ func: T,
147
+ wait: number,
148
+ options: ThrottleOptions = {}
149
+ ): T & { cancel: () => void; flush: () => ReturnType<T> | undefined } {
150
+ const { leading = true, trailing = true } = options;
151
+ return debounce(func, wait, { leading, trailing, maxWait: wait });
152
+ }
153
+
154
+ /**
155
+ * Request animation frame based throttle
156
+ */
157
+ export function rafThrottle<T extends (...args: unknown[]) => unknown>(
158
+ func: T
159
+ ): T & { cancel: () => void } {
160
+ let rafId: number | null = null;
161
+ let lastArgs: Parameters<T> | undefined;
162
+ let lastThis: unknown;
163
+
164
+ function throttled(this: unknown, ...args: Parameters<T>): void {
165
+ lastArgs = args;
166
+ lastThis = this;
167
+
168
+ if (rafId === null) {
169
+ rafId = requestAnimationFrame(() => {
170
+ rafId = null;
171
+ if (lastArgs) {
172
+ func.apply(lastThis, lastArgs);
173
+ lastArgs = undefined;
174
+ lastThis = undefined;
175
+ }
176
+ });
177
+ }
178
+ }
179
+
180
+ function cancel(): void {
181
+ if (rafId !== null) {
182
+ cancelAnimationFrame(rafId);
183
+ rafId = null;
184
+ }
185
+ lastArgs = undefined;
186
+ lastThis = undefined;
187
+ }
188
+
189
+ throttled.cancel = cancel;
190
+
191
+ return throttled as T & { cancel: () => void };
192
+ }
193
+
194
+ /**
195
+ * Idle callback based debounce
196
+ */
197
+ export function idleDebounce<T extends (...args: unknown[]) => unknown>(
198
+ func: T,
199
+ timeout: number = 5000
200
+ ): T & { cancel: () => void } {
201
+ let idleId: number | null = null;
202
+ let timeoutId: number | null = null;
203
+ let lastArgs: Parameters<T> | undefined;
204
+ let lastThis: unknown;
205
+
206
+ function debounced(this: unknown, ...args: Parameters<T>): void {
207
+ lastArgs = args;
208
+ lastThis = this;
209
+
210
+ // Cancel previous calls
211
+ if (idleId !== null) {
212
+ cancelIdleCallback(idleId);
213
+ idleId = null;
214
+ }
215
+ if (timeoutId !== null) {
216
+ clearTimeout(timeoutId);
217
+ timeoutId = null;
218
+ }
219
+
220
+ // Try to use idle callback
221
+ if (typeof requestIdleCallback !== 'undefined') {
222
+ idleId = requestIdleCallback(() => {
223
+ idleId = null;
224
+ if (lastArgs) {
225
+ func.apply(lastThis, lastArgs);
226
+ lastArgs = undefined;
227
+ lastThis = undefined;
228
+ }
229
+ }, { timeout });
230
+ } else {
231
+ // Fallback to setTimeout
232
+ timeoutId = window.setTimeout(() => {
233
+ timeoutId = null;
234
+ if (lastArgs) {
235
+ func.apply(lastThis, lastArgs);
236
+ lastArgs = undefined;
237
+ lastThis = undefined;
238
+ }
239
+ }, 16); // ~60fps fallback
240
+ }
241
+ }
242
+
243
+ function cancel(): void {
244
+ if (idleId !== null) {
245
+ cancelIdleCallback(idleId);
246
+ idleId = null;
247
+ }
248
+ if (timeoutId !== null) {
249
+ clearTimeout(timeoutId);
250
+ timeoutId = null;
251
+ }
252
+ lastArgs = undefined;
253
+ lastThis = undefined;
254
+ }
255
+
256
+ debounced.cancel = cancel;
257
+
258
+ return debounced as T & { cancel: () => void };
259
+ }
@@ -0,0 +1,371 @@
1
+ /**
2
+ * Performance utilities for ProteusJS
3
+ * Provides timing, measurement, and optimization tools
4
+ */
5
+
6
+ export interface TimingMark {
7
+ name: string;
8
+ startTime: number;
9
+ endTime?: number;
10
+ duration?: number;
11
+ metadata?: Record<string, unknown>;
12
+ }
13
+
14
+ export interface PerformanceBudget {
15
+ responseTime: number; // Maximum response time in ms
16
+ frameRate: number; // Minimum frame rate
17
+ memoryUsage: number; // Maximum memory usage in MB
18
+ }
19
+
20
+ export class PerformanceTracker {
21
+ private marks: Map<string, TimingMark> = new Map();
22
+ private measurements: TimingMark[] = [];
23
+ private budget: PerformanceBudget;
24
+ private warningThreshold: number = 0.8; // 80% of budget
25
+
26
+ constructor(budget?: Partial<PerformanceBudget>) {
27
+ this.budget = {
28
+ responseTime: 60, // 60ms default
29
+ frameRate: 60, // 60fps default
30
+ memoryUsage: 100, // 100MB default
31
+ ...budget
32
+ };
33
+ }
34
+
35
+ /**
36
+ * Start timing a performance mark
37
+ */
38
+ public mark(name: string, metadata?: Record<string, unknown>): void {
39
+ const mark: TimingMark = {
40
+ name,
41
+ startTime: performance.now(),
42
+ ...(metadata && { metadata })
43
+ };
44
+
45
+ this.marks.set(name, mark);
46
+
47
+ // Use Performance API if available
48
+ if (typeof performance.mark === 'function') {
49
+ performance.mark(`proteus-${name}-start`);
50
+ }
51
+ }
52
+
53
+ /**
54
+ * End timing a performance mark
55
+ */
56
+ public measure(name: string): TimingMark | null {
57
+ const mark = this.marks.get(name);
58
+ if (!mark) {
59
+ console.warn(`ProteusJS: Performance mark "${name}" not found`);
60
+ return null;
61
+ }
62
+
63
+ const endTime = performance.now();
64
+ const duration = endTime - mark.startTime;
65
+
66
+ const measurement: TimingMark = {
67
+ ...mark,
68
+ endTime,
69
+ duration
70
+ };
71
+
72
+ this.measurements.push(measurement);
73
+ this.marks.delete(name);
74
+
75
+ // Use Performance API if available
76
+ if (typeof performance.mark === 'function' && typeof performance.measure === 'function') {
77
+ performance.mark(`proteus-${name}-end`);
78
+ performance.measure(`proteus-${name}`, `proteus-${name}-start`, `proteus-${name}-end`);
79
+ }
80
+
81
+ // Check against budget
82
+ this.checkBudget(measurement);
83
+
84
+ return measurement;
85
+ }
86
+
87
+ /**
88
+ * Get all measurements
89
+ */
90
+ public getMeasurements(): TimingMark[] {
91
+ return [...this.measurements];
92
+ }
93
+
94
+ /**
95
+ * Get measurements by name pattern
96
+ */
97
+ public getMeasurementsByPattern(pattern: RegExp): TimingMark[] {
98
+ return this.measurements.filter(m => pattern.test(m.name));
99
+ }
100
+
101
+ /**
102
+ * Get average duration for measurements with the same name
103
+ */
104
+ public getAverageDuration(name: string): number {
105
+ const matching = this.measurements.filter(m => m.name === name && m.duration !== undefined);
106
+ if (matching.length === 0) return 0;
107
+
108
+ const total = matching.reduce((sum, m) => sum + (m.duration || 0), 0);
109
+ return total / matching.length;
110
+ }
111
+
112
+ /**
113
+ * Get performance statistics
114
+ */
115
+ public getStats(): Record<string, unknown> {
116
+ const stats: Record<string, unknown> = {
117
+ totalMeasurements: this.measurements.length,
118
+ activeMeasurements: this.marks.size,
119
+ budget: this.budget,
120
+ violations: this.getBudgetViolations()
121
+ };
122
+
123
+ // Group by name
124
+ const byName: Record<string, { count: number; avgDuration: number; maxDuration: number }> = {};
125
+
126
+ this.measurements.forEach(m => {
127
+ if (m.duration === undefined) return;
128
+
129
+ if (!byName[m.name]) {
130
+ byName[m.name] = { count: 0, avgDuration: 0, maxDuration: 0 };
131
+ }
132
+
133
+ byName[m.name]!.count++;
134
+ byName[m.name]!.maxDuration = Math.max(byName[m.name]!.maxDuration, m.duration);
135
+ });
136
+
137
+ // Calculate averages
138
+ Object.keys(byName).forEach(name => {
139
+ byName[name]!.avgDuration = this.getAverageDuration(name);
140
+ });
141
+
142
+ stats['byName'] = byName;
143
+ return stats;
144
+ }
145
+
146
+ /**
147
+ * Clear all measurements
148
+ */
149
+ public clear(): void {
150
+ this.measurements.length = 0;
151
+ this.marks.clear();
152
+ }
153
+
154
+ /**
155
+ * Update performance budget
156
+ */
157
+ public updateBudget(budget: Partial<PerformanceBudget>): void {
158
+ this.budget = { ...this.budget, ...budget };
159
+ }
160
+
161
+ /**
162
+ * Check measurement against budget
163
+ */
164
+ private checkBudget(measurement: TimingMark): void {
165
+ if (measurement.duration === undefined) return;
166
+
167
+ const warningThreshold = this.budget.responseTime * this.warningThreshold;
168
+ const errorThreshold = this.budget.responseTime;
169
+
170
+ if (measurement.duration > errorThreshold) {
171
+ console.error(
172
+ `ProteusJS: Performance budget exceeded for "${measurement.name}": ${measurement.duration.toFixed(2)}ms > ${errorThreshold}ms`
173
+ );
174
+ } else if (measurement.duration > warningThreshold) {
175
+ console.warn(
176
+ `ProteusJS: Performance warning for "${measurement.name}": ${measurement.duration.toFixed(2)}ms > ${warningThreshold.toFixed(2)}ms`
177
+ );
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Get budget violations
183
+ */
184
+ private getBudgetViolations(): TimingMark[] {
185
+ return this.measurements.filter(m =>
186
+ m.duration !== undefined && m.duration > this.budget.responseTime
187
+ );
188
+ }
189
+ }
190
+
191
+ /**
192
+ * Global performance tracker instance
193
+ */
194
+ export const performanceTracker = new PerformanceTracker();
195
+
196
+ /**
197
+ * Decorator for automatic performance tracking
198
+ */
199
+ export function trackPerformance(name?: string): (target: unknown, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor {
200
+ return function (target: unknown, propertyKey: string, descriptor: PropertyDescriptor): PropertyDescriptor {
201
+ const originalMethod = descriptor.value;
202
+ const trackingName = name || `${(target as { constructor: { name: string } }).constructor.name}.${propertyKey}`;
203
+
204
+ descriptor.value = function (...args: unknown[]): unknown {
205
+ performanceTracker.mark(trackingName);
206
+
207
+ try {
208
+ const result = originalMethod.apply(this, args);
209
+
210
+ // Handle async methods
211
+ if (result && typeof result.then === 'function') {
212
+ return result.finally(() => {
213
+ performanceTracker.measure(trackingName);
214
+ });
215
+ }
216
+
217
+ performanceTracker.measure(trackingName);
218
+ return result;
219
+ } catch (error) {
220
+ performanceTracker.measure(trackingName);
221
+ throw error;
222
+ }
223
+ };
224
+
225
+ return descriptor;
226
+ };
227
+ }
228
+
229
+ /**
230
+ * Measure function execution time
231
+ */
232
+ export function measureTime<T extends (...args: unknown[]) => unknown>(
233
+ func: T,
234
+ name?: string
235
+ ): T {
236
+ const measureName = name || func.name || 'anonymous';
237
+
238
+ return ((...args: unknown[]) => {
239
+ performanceTracker.mark(measureName);
240
+
241
+ try {
242
+ const result = func(...args);
243
+
244
+ // Handle async functions
245
+ if (result && typeof (result as { then?: unknown }).then === 'function') {
246
+ return (result as Promise<unknown>).finally(() => {
247
+ performanceTracker.measure(measureName);
248
+ });
249
+ }
250
+
251
+ performanceTracker.measure(measureName);
252
+ return result;
253
+ } catch (error) {
254
+ performanceTracker.measure(measureName);
255
+ throw error;
256
+ }
257
+ }) as T;
258
+ }
259
+
260
+ /**
261
+ * Create a performance-aware wrapper for frequently called functions
262
+ */
263
+ export function createPerformanceWrapper<T extends (...args: unknown[]) => unknown>(
264
+ func: T,
265
+ options: {
266
+ name?: string;
267
+ sampleRate?: number; // 0-1, percentage of calls to measure
268
+ budget?: number; // ms
269
+ } = {}
270
+ ): T {
271
+ const { name = func.name || 'wrapped', sampleRate = 0.1, budget = 16 } = options;
272
+ let callCount = 0;
273
+ let totalTime = 0;
274
+ let maxTime = 0;
275
+
276
+ return ((...args: unknown[]) => {
277
+ callCount++;
278
+ const shouldMeasure = Math.random() < sampleRate;
279
+
280
+ if (shouldMeasure) {
281
+ const startTime = performance.now();
282
+
283
+ try {
284
+ const result = func(...args);
285
+
286
+ const endTime = performance.now();
287
+ const duration = endTime - startTime;
288
+
289
+ totalTime += duration;
290
+ maxTime = Math.max(maxTime, duration);
291
+
292
+ if (duration > budget) {
293
+ console.warn(
294
+ `ProteusJS: Performance budget exceeded in ${name}: ${duration.toFixed(2)}ms > ${budget}ms`
295
+ );
296
+ }
297
+
298
+ return result;
299
+ } catch (error) {
300
+ const endTime = performance.now();
301
+ const duration = endTime - startTime;
302
+ totalTime += duration;
303
+ maxTime = Math.max(maxTime, duration);
304
+ throw error;
305
+ }
306
+ } else {
307
+ return func(...args);
308
+ }
309
+ }) as T;
310
+ }
311
+
312
+ /**
313
+ * Get browser performance information
314
+ */
315
+ export function getBrowserPerformanceInfo(): object {
316
+ const info: any = {
317
+ timing: {},
318
+ memory: {},
319
+ navigation: {}
320
+ };
321
+
322
+ // Navigation timing (using modern API)
323
+ try {
324
+ const navigationEntries = performance.getEntriesByType('navigation') as PerformanceNavigationTiming[];
325
+ if (navigationEntries.length > 0) {
326
+ const nav = navigationEntries[0]!;
327
+ info.timing = {
328
+ domContentLoaded: nav.domContentLoadedEventEnd - nav.domContentLoadedEventStart,
329
+ loadComplete: nav.loadEventEnd - nav.loadEventStart,
330
+ domInteractive: nav.domInteractive - nav.fetchStart
331
+ };
332
+ }
333
+ } catch (error) {
334
+ // Modern browsers should support performance.getEntriesByType
335
+ // If not available, we'll just skip the timing information
336
+ console.warn('ProteusJS: Performance timing API not available', error);
337
+ }
338
+
339
+ // Memory info
340
+ if ('memory' in performance) {
341
+ const memory = (performance as any).memory;
342
+ info.memory = {
343
+ usedJSHeapSize: memory.usedJSHeapSize,
344
+ totalJSHeapSize: memory.totalJSHeapSize,
345
+ jsHeapSizeLimit: memory.jsHeapSizeLimit
346
+ };
347
+ }
348
+
349
+ // Navigation info (using modern API)
350
+ try {
351
+ const navigationEntries = performance.getEntriesByType('navigation') as PerformanceNavigationTiming[];
352
+ if (navigationEntries.length > 0) {
353
+ const nav = navigationEntries[0]!;
354
+ info.navigation = {
355
+ type: nav.type === 'navigate' ? 0 : nav.type === 'reload' ? 1 : nav.type === 'back_forward' ? 2 : 255,
356
+ redirectCount: nav.redirectCount || 0
357
+ };
358
+ }
359
+ } catch (error) {
360
+ // Fallback for older browsers (deprecated APIs)
361
+ const perfAny = performance as any;
362
+ if (perfAny.navigation) {
363
+ info.navigation = {
364
+ type: perfAny.navigation.type,
365
+ redirectCount: perfAny.navigation.redirectCount
366
+ };
367
+ }
368
+ }
369
+
370
+ return info;
371
+ }